首页 > 解决方案 > Let vs. Haskell 中的 Lambda

问题描述

我正在阅读 Will Kurt 的“使用 Haskell 进行编程”。在关于词法作用域的第 3 课结束时,作者写道:

使用let表达式和 lambda 函数在本质上并不完全相同。例如,如果您尝试运行以下代码,则会导致错误:

counter x = let x = x + 1 in let x = x + 1 in x

为了证明let和 lambda 不同,请完全按照此处的方式重写 counter 函数,但使用嵌套的 lambdas 而不是let.

这是我的解决方案,它可以按我的预期工作:

counterLambda x = (\x -> (\x -> x) (x + 1)) (x + 1)
-- counterLambda 2 == 4

但是,正如作者建议的那样,如果我counter 2在 ghci 中运行,它会永远挂起(使用 GHC 8.8.3)。

引擎盖下发生了什么?


PS:当我正确命名变量时它可以工作。

counter x = let a = x + 1 in let b = a + 1 in b
-- counter 2 == 4

标签: haskell

解决方案


在 lambdas 和lets 中,每个都x遮盖了之前的那个。区别在于阴影的范围。lambda 参数的范围仅限于 lambda,但 let 绑定的范围是整个let ... = ... in ...结构,因此根据自身let x = x + 1定义,而根据unshadowed定义阴影。让我通过向 each 添加一个数字来演示每个实现的影子范围:x(\x -> x) (x + 1)xxx

counterLambda x0 = (\x1 -> (\x2 -> (\x3 -> x3) x2) (x1 + 1)) (x0 + 1)

counter       x0 = let x1 = x1 + 1 in let x2 = x2 + 1 in x2

现在应该清楚为什么这些不同了。在 lambda 版本中,x1is assigned x0 + 1,而在let版本中,x1is assigned x1 + 1,它不会终止。


推荐阅读