How do I make a Constant Applicative Form into, well, not a Constant Applicative Form, to stop it being retained for the lifetime of the program?
I've tried this approach:
-- | Dummy parameter to avoid creating a CAF twoTrues :: () -> [[[Bool]]] twoTrues _ = map (++ (True : repeat False)) . trueBlock <$> [1..]
but it doesn't seem to work - the profile shows it as still being retained and still marks it as a CAF.
I've found one relevant Google result on this, a reply by Simon Peyton-Jones to Neil Mitchell who asked precisely this question - but that answer refers to a dead link, unfortunately.
A complete example
Here's a little example that shows the situation:
module A where big :: () -> [Int] big _ = [1..10^7]
Looks like a function, right? But what does GHC do? It floats the enum to the top level!
A.big1 :: [Int] [ Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False, ConLike=False, Cheap=False, Expandable=False, Guidance=IF_ARGS [] 7 0}] A.big1 = case A.$wf1 10 A.big2 of ww_sDD { __DEFAULT -> eftInt 1 ww_sDD } A.big :: () -> [Int] [Arity=1, Unf=Unf{Src=InlineStable, TopLvl=True, Arity=1, Value=True, ConLike=True, Cheap=True, Expandable=True, Guidance=ALWAYS_IF(unsat_ok=True,boring_ok=True) Tmpl= \ _ -> A.big1}] A.big = \ _ -> A.big1
Ooops!
So what can we do?
Turn off optimizations
That works, -Onot
, but not desirable:
A.big :: () -> [Int] [GblId, Arity=1] A.big = \ _ -> enumFromTo @ Int $fEnumInt (I# 1) (^ @ Int @ Type.Integer $fNumInt $fIntegralInteger (I# 10) (smallInteger 7))
Don't inline, and more functons
Make everything into a function, including the enumFromTo
, plumbing the parameter through to the workers:
big :: () -> [Int] big u = myEnumFromTo u 1 (10^7) {-# NOINLINE big #-} myEnumFromTo :: () -> Int -> Int -> [Int] myEnumFromTo _ n m = enumFromTo n m {-# NOINLINE myEnumFromTo #-}
Now we are finally CAF-free! Even with -O2
A.myEnumFromTo [InlPrag=NOINLINE] :: () -> Int -> Int -> [Int] A.myEnumFromTo = \ _ (n_afx :: Int) (m_afy :: Int) -> $fEnumInt_$cenumFromTo n_afx m_afy A.big [InlPrag=NOINLINE] :: () -> [Int] A.big = \ (u_abx :: ()) -> A.myEnumFromTo u_abx A.$s^2 lvl3_rEe
Yay.
What doesn't work?
Turn off -ffull-laziness
The full laziness transformation floats definitions outwards. It is on by default with -O1
or above. Let's try turning it off with -fno-full-laziness
. However, it doesn't work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With