完全懒惰
repeatedly
demonstrated

cause
space
leaks

为什么从-O开始完全懒惰?我发现自己对SPJ的The Implementation of Functional Programming Languages中的推理不满意。声称是在

f = \y -> y + sqrt 4

每次输入sqrt 4时,不必要重复f,因此我们应将其 float 到lambda之外。我同意小公司的观点,但是由于我们已经看到这种转变在大公司中造成了什么问题,所以我认为这样做是不值得的。在我看来,这种转换的好处是可以单方面获得的,**仅需对本地代码进行更改,而希望它的程序员应手动实现它。

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

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

最佳答案

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

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

在这种情况下,您只能进行eta还原,但通常不能进行eta还原。并且命名函数(+1)非常难看。

07-26 00:59