Haskell 是否在懒惰评估期间丢弃中间结果?

Does Haskell discards intermediary results during lazy evaluation?

如果我递归定义斐波那契数列:

fibo_lazy_list = 0 : 1 : zipWith (+) fibo_lazy_list (tail fibo_lazy_list)

然后要求第一个大于给定值的元素,比如:

print $ find (>100) fibo_lazy_list

我了解 Haskell 仅评估获得打印结果所需的元素。但是在打印之前它们是否都保存在内存中?由于只需要列表中的两个元素来计算最后一个元素,Haskell 是释放最左边的元素还是列表在内存中不断增长?

视情况而定

这实际上是正确处理真实世界代码的最棘手的事情之一 Haskell:为了避免因保留不必要的数据而导致的内存泄漏,这本来应该是中间的,但事实证明实际上是对一些尚未评估的惰性 thunk 的依赖,因此不能被垃圾收集。

在您的示例中,fibo_lazy_list 的前导元素(顺便说一句,请使用 camelCase,而不是 Haskell 中的 underscore_case),只要因为 fibo_lazy_list 被仍然可以评估的东西所引用。但是一旦它超出范围,那是不可能的。所以如果你这样写

print $ let fibo_lazy_list = 0 : 1 : zipWith (+) fibo_lazy_list (tail fibo_lazy_list)
        in find (>100) fibo_lazy_list

那么您可以非常确信未使用的元素 将被垃圾收集,甚至可能在找到要打印的元素之前。

但是,如果 fibo_lazy_list 是在顶层定义的,并且是 CAF(如果类型不是多态的话)

fiboLazyList :: [Integer]
fiboLazyList = 0 : 1 : zipWith (+) fiboLazyList (tail fiboLazyList)

main :: IO ()
main = do
   ...
   print $ find (>100) fiboLazyList
   ...

那么你最好期望所有前导元素即使在 >100 被提取后仍保留在内存中。

编译器优化可能在这里有用,严格注释也可以。但正如我所说,这在 Haskell 中有点痛苦。