Allow the user to prevent floating and CSE
This is a write-up of a rough idea that Andres Löh and me had at ICFP 2016 in order to address some Real World problems Andres noticed and that are currently hard to avoid.
The goal is to give the user more control about expressions that the compiler would like to float out (or CSE), but the programmer knows better. Example (assume no list fusion exists):
enum xs = zip [1..] xs
This leads to a horrible space leak, as GHC will float out [1..]
to the top.
Our idea is to have a magic function nofloat :: a -> a
(magic in the same sense as inline
and lazy
) that the programmer would use here:
enum xs = zip (nofloat [1..]) xs
With these effects:
- Sub expressions are not floated out of a
nofloat
. - An expression of the form
nofloat e
would not be floated beyond the innermost enclosing lambda. - Two expressions of the form
nofloat e
would not be commoned up by CSE.
This way, unwanted sharing is prevented.
In contrast to a hypothetical veryCheap
function, it does not mean that the compiler should float it into lambda (no unwanted duplication either).
Two open questions (among many others, I am sure:)
- Likely, rule matching should look through
nofloat
. At least in this example (and similar ones likemap (nofloat [1..])
, the rules in question will avoid the spaceleaks). - Possibly, nothing should be floated (inlined) into a
nofloat
. Rationale: Assume the library is changed so that
[n..] = nofloat (realEnumFrom n) {-# INLINE [n..] #-}
Then
zip [fib 1000..]
would be rewritten by the inliner tozip (let x = fib 1000 in (nofloat [x..]))
. Moving thefib 1000
into thenofloat
would change the behaviour in a possibly surprising way.