【发布时间】:2014-08-13 06:27:15
【问题描述】:
我正在开发一个小型 lambda 演算引擎,我希望它像 Haskell 一样懒惰。我正在努力,至少现在,坚持 Haskell 的规则,这样我就不必重新考虑所有事情,但我不想盲目地这样做。
我知道 Haskell 不会评估一个术语,直到需要它的值。这是我的第一个疑问。我知道当是内置函数的参数时,值是“需要”的(所以在(func x) 中,如果func 是内置函数并且需要(func x),则需要x)或者因为它是一个函数被调用(所以在(x y)中,如果需要(x y),则需要x)。
我的第二个问题是,假设我有这个递归函数:
let st = \x -> (st x)
到目前为止,我实现它的方式是,如果我将其称为 (st "hi"),"hi" 将不会被评估,而是包装在包含术语及其范围的 thunk 中,它将添加为“ x" 到 st 的主体范围内。然后,当再次评估st 时,将围绕(st x) 中的x 创建另一个thunk,其中将包含术语x 及其范围,其中包含x 的另一个定义,即“hi ”。这样,嵌套的 thunk 将继续建立,直到内存不足。
我在 GHCI 中测试了上面的代码,内存正常。然后我测试了这个:
let st = \x -> (st (id x))
和内存建立,直到应用程序崩溃。所以显然 GHCI(或 Haskell?)仅在参数是函数调用时才使用 thunk;在所有其他情况下,它都使用该术语的值。这是我可以轻松实现的。
我想到的另一个选项是不允许嵌套 thunk,在评估函数调用并为参数创建 thunk 之前,评估整个当前范围以确保没有新的 thunk 将包含另一个 thunk。我认为这仍然可以让我创建无限列表并获得惰性评估的一些(或全部?)好处,甚至可以防止应用程序在调用 let st = \x.(st (id x)) 函数时崩溃。
我确信有很多方法可以实现惰性求值,但很难弄清楚每种方法的优缺点。是否有一些列表包含最常见的惰性评估实现及其优缺点?还有,Haskell 是怎么做到的?
【问题讨论】:
-
阅读弱头范式等。例如。 Simon Marlow's book discusses the topic nicely.
标签: haskell lazy-evaluation lambda-calculus