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

Tom*_*lis 11 haskell ghc

全懒惰已经 反复 证明 ,以 使 空间 的泄漏.

为什么从一-O开始就充分懒惰?我发现自己不相信SPJ的"函数式编程语言的实现"中的推理.声称是在

f = \y -> y + sqrt 4
Run Code Online (Sandbox Code Playgroud)

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

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

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

Rei*_*ton 10

至少有一种常见的情况是完全懒惰是"安全的"和优化.

g :: Int -> Int
g z = f (z+1)
  where f 0 = 0
        f y = 1 + f (y-1)
Run Code Online (Sandbox Code Playgroud)

这实际上意味着,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)
Run Code Online (Sandbox Code Playgroud)

其中不需要分配来调用g_f.令人高兴的是,完全懒惰的转变确实如此.

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

另一个例子:

h :: [Int] -> [Int]
h xs = map (+1) xs
Run Code Online (Sandbox Code Playgroud)

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

  • 请注意,浮出 lambda 应该*总是*有益的。浮动重击或部分应用程序可能会产生您想要消除的丑陋副作用(保留重建成本低廉的巨大数据结构)。遗憾的是 GHC 错过了一个标志。 (2认同)