How to make a CAF not a CAF in Haskell?

前端 未结 7 1098
南旧
南旧 2020-12-01 05:23

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 ap

7条回答
  •  广开言路
    2020-12-01 06:13

    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=, 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.

提交回复
热议问题