编辑:忽略这个答案。它仅在您未经优化进行编译时才有效。以我的 fibonacci 实现为例,ghc -O0 -ddump-simpl main.hs
在 Core 中给出了一个 fib 定义:
Main.fib :: forall a_agB. GHC.Num.Num a_agB => () -> [a_agB]
O2
但是使用优化进行编译ghc -O2 -ddump-simpl main.hs
会将斐波那契列表实现为顶层的常量值
Main.$wfib :: forall a_agG. GHC.Num.Num a_agG => [a_agG]
然后它被封闭的斐波那契实现调用(它确实需要一个()
参数)
Main.fib =
\ (@ a_agG) (w_stM :: GHC.Num.Num a_agG) (w1_stN :: ()) ->
case w1_stN of _ { () -> Main.$wfib @ a_agG w_stM }
结果是,虽然Main.fib
没有被记忆,Main.$wfib
但仍会被记忆并占用内存。
原答案如下:
正如丹尼尔瓦格纳在另一个答案中所说,这样的定义
val = 1+2+3
只会评估一次,除非它是多态的。如果您想在每次引用它时对其进行评估,您可以这样做
val () = 1+2+3
这样,您必须将参数提供()
给val
,但它不会保存计算值。这可能是一件好事,如果 的计算值val
占用大量内存,但易于计算,并且在您需要时逐渐消耗。例如,您可以有一个斐波那契数列:
fib = 0 : 1 : zipWith (+) fib (tail fib)
如果您现在这样做fib !! 100000
,您将需要计算所有前 100000 个斐波那契数。该值fib
引用此列表,因此它不能被垃圾收集,但会在内存中徘徊。为了解决这个问题,你可以这样做
fib () = let x = 0 : 1 : zipWith (+) x (tail x) in x
由于fib
现在是(常量)函数而不是常量值,因此不会保存其值,从而释放内存。
编辑:正如 Philip JF 在评论中所说,let 表达式的内容可能会被不友好的编译器提升,这会导致不必要的共享