为什么全懒惰是默认优化?

时间:2016-01-31 14:52:33

标签: haskell ghc

完全懒惰了 repeatedly demonstratedcause space leaks

为什么从-O开始完全懒惰?我发现自己不相信SPJ的The Implementation of Functional Programming Languages中的推理。

中的主张是
f = \y -> y + sqrt 4
每次输入sqrt 4时都会不必要地重复{p> f,所以我们应该将它浮动到lambda之外。我同意这一点,但是因为我们已经看到了这种转变导致的大问题我不相信它是值得的。在我看来,这种转换的好处可以单方面获得**只需要本地代码更改,而想要它的程序员应该手动实现它。

你能说服我吗? full-laziness实际上真的有用吗?如果你能提供需要多边合作或非地方转型的手工实施的例子,我将特别相信。

**与内联和流融合等优化不同,手动实现需要模块之间的多边合作和非本地代码更改

1 个答案:

答案 0 :(得分:10)

至少有一种常见的情况是完全懒惰是“安全”和优化。

g :: Int -> Int
g z = f (z+1)
  where f 0 = 0
        f y = 1 + f (y-1)

这实际上意味着g = \z -> let {f = ...} in f (z+1)并且以这种方式编译,在调用它之前将为f分配一个闭包。显然这很愚蠢,编译器应该将程序转换为

g_f 0 = 0
g_f y = 1 + g_f (y-1)
g z = g_f (z+1)

不需要分配来调用g_f。令人高兴的是,完全懒惰的转变就是这样。

显然程序员可以避免制作这些不依赖于顶级函数参数的局部定义,但这些定义通常被认为是好的风格......

另一个例子:

h :: [Int] -> [Int]
h xs = map (+1) xs

在这种情况下,你可以减少,但通常你不能减少。命名函数(+1)非常难看。