EDIT: Disregard this answer. It only works when you compile without optimization. Taking my fibonacci implementation as an example, ghc -O0 -ddump-simpl main.hs
gives a single fib definition in Core:
Main.fib :: forall a_agB. GHC.Num.Num a_agB => () -> [a_agB]
but compiling with O2
optimizations ghc -O2 -ddump-simpl main.hs
implements the fibonacci list as a constant value at the top level
Main.$wfib :: forall a_agG. GHC.Num.Num a_agG => [a_agG]
This then gets called by the enclosing fibonacci implementation (which does take a ()
argument)
Main.fib =
\ (@ a_agG) (w_stM :: GHC.Num.Num a_agG) (w1_stN :: ()) ->
case w1_stN of _ { () -> Main.$wfib @ a_agG w_stM }
The result is, that while Main.fib
doesn't get memoized, Main.$wfib
will still be memoized and take up memory.
Original answer below:
As Daniel Wagner said in another answer, a definition such as this
val = 1+2+3
would only evaluate once, unless it was polymorphic. If you want to evaluate it every time you refer to it, you could do
val () = 1+2+3
This way you have to give the argument ()
to val
, but then it won't save the computed value. This can be a good thing, if the computed value of val
takes a lot of memory, but is easy to compute, and is consumed incrementally when you need it. For instance, you could have a list of the fibonacci numbers:
fib = 0 : 1 : zipWith (+) fib (tail fib)
If you now do fib !! 100000
, you will need to compute all the first 100000 fibonacci numbers. The value fib
refers to this list, so it can't be garbage collected, but will hang around in memory. To counter this, you could do
fib () = let x = 0 : 1 : zipWith (+) x (tail x) in x
Since fib
is now a (constant) function instead of a constant value, its value will not be saved, freeing up memory.
Edit: As Philip JF said in the comments, the content of the let expression might get lifted by an unfriendly compiler which would cause unwanted sharing