Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a CAF not a CAF in Haskell?

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.

like image 906
Robin Green Avatar asked May 22 '11 21:05

Robin Green


1 Answers

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.

like image 56
Don Stewart Avatar answered Nov 09 '22 23:11

Don Stewart