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 ewould not be floated beyond the innermost enclosing lambda. - Two expressions of the form
nofloat ewould 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 1000into thenofloatwould change the behaviour in a possibly surprising way.