这个 let 表达式是如何工作的?

How does this let expression work?

鉴于这行 Haskell 代码,我的任务是将其评估为最简单的形式。

let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20

我已经得到了答案(当然我自己在 GHCI 中评估过):42

但是,我想在这里更好地了解评估的实际运作方式。总的来说,我想我知道如何(简单地)让表达式工作:

例子

a = let y = 5 in y * 5  -- a == 25

这计算为 25 因为我们将 y 绑定到 5 的值并且 a 被分配给 y*5 的值(部分in 之后)。绑定 y = 5 仅在 let.

范围内有效

到目前为止,唯一的解释(至少评估为 42)如下:

let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20

但是令我困惑的是在 let 之后使用 g h k。这是什么意思?

g h k = ... 是一个函数定义。这意味着将 g 应用于两个参数(名为 hk)的结果将评估为 ... 部分。换句话说,它是 g = \h -> \k -> ....

的快捷方式

所以我们可以将表达式一步步简化为:

let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20
let g = \h -> \k -> (\x -> k (h x)) in g (+1) (\x -> x+x) 20
(\h -> \k -> (\x -> k (h x))) (+1) (\x -> x+x) 20
(\k -> (\x -> k ((+1) x))) (\x -> x+x) 20
(\x -> (\x -> x+x) ((+1) x)) 20
(\x -> x+x) ((+1) 20)
(\x -> x+x) 21
21 + 21
42

想想函数定义。如果你写:

g h k x = k (h x)

那么就是一个带三个参数的函数hkx以及returnsk (h x)。这相当于:

g h k = \x -> k (h x)

或:

g h = \k x -> k (h x)

或:

g = \h k x -> k (h x)

所以我们可以在函数头部和主体中的 lambda 表达式之间传递变量。事实上 Haskell 编译器会重写它。

因此,对于 let 表达式,我们定义了一个局部范围的函数,就像上面定义的那样。现在如果我们调用 g (+1) (\x -> x+x) 20,那么将调用 gh = (+1)k = (\x -> x+x)x = 20

所以我们将其评价为:

(\x -> x+x) ((+1) 20)

计算结果为:

   (\x -> x+x) ((+1) 20)
-> ((+1) 20)+((+1) 20)
-> 21 + 21
-> 42