为什么在 do 块中需要 let 关键字?
Why do you need let keyword in do block?
do
块中有 let
的原因是什么。
-- codeblock A
main = do
let a = 0
let f a = a + 1
let b = f 0
print (a,b)
-- codeblock B
main = do
a = 0
f a = a + 1
b = f 0
print (a,b)
假设所有没有 in
的 let
后面必须跟着 =
(这是真的吗?)
编译器应该能够暗示 let
从 =
和 preprocess/de-sugar codeblock B
到 codeblock A
在这种情况下使用 let
似乎是不必要的,就像你可以写 codeblock C
但选择写 codeblock D
-- codeblock C
main = do
print (a,b)
a = 0
f a = a + 1
b = f 0
-- codeblock D
main = do
print (a,b)
function a = 0
function f a = a + 1
function b = f 0
澄清一下,我的假设不包括 let
,然后是 in
,应该保持不变。
-- codeblock E
main = do
a = 0
f a = a + 1
b = f 0
c = let d = 1
e = 1
in d + e
print (a,b,c)
支持 let
的一个论点是它在 do
块中更突出,否则可能会填充各种类似单子赋值的运算符(想想用 operators defined in lens
).
do
p1.x += delta -- (+=) is a custom operator
p2.y -= delta -- (-=) is a custom operator
let delta' = delta*delta
p3 .= Point delta' delta' -- (.=) is a custom operator
由于 let
.
,我可以很容易地将抽象 delta'
的语法糖与实际的 monadic 代码区分开来
我不知道为什么这样做,但这是我可以想象的一个原因:它允许您指定应按顺序建立哪些绑定,以及同时建立哪些绑定,这在阴影的情况下可能很重要。
例如,假设你的建议得到了实施,然后考虑:
foo :: [Int]
foo = do
x <- return [1]
y = 0:x
x = [1..5]
y
有两种合理的脱糖方法:
foo1 :: [Int]
foo1 = do
x <- return [1]
let y = 0:x
let x = [1..5]
y
foo2 :: [Int]
foo2 = do
x <- return [1]
let y = 0:x
x = [1..5]
y
foo1
的计算结果为 [0,1]
,foo2
的计算结果为 [0,1,2,3,4,5]
。当然,这是一种编写代码的奇怪方式,但 let
必须明确这一事实意味着您的意图没有歧义。
正如 在评论中指出的那样,阴影并不是您可能需要明确说明 let
绑定如何分组的唯一原因:函数定义可能需要多个方程式,以匹配多个参数模式。
do
块中有 let
的原因是什么。
-- codeblock A
main = do
let a = 0
let f a = a + 1
let b = f 0
print (a,b)
-- codeblock B
main = do
a = 0
f a = a + 1
b = f 0
print (a,b)
假设所有没有 in
的 let
后面必须跟着 =
(这是真的吗?)
编译器应该能够暗示 let
从 =
和 preprocess/de-sugar codeblock B
到 codeblock A
在这种情况下使用 let
似乎是不必要的,就像你可以写 codeblock C
但选择写 codeblock D
-- codeblock C
main = do
print (a,b)
a = 0
f a = a + 1
b = f 0
-- codeblock D
main = do
print (a,b)
function a = 0
function f a = a + 1
function b = f 0
澄清一下,我的假设不包括 let
,然后是 in
,应该保持不变。
-- codeblock E
main = do
a = 0
f a = a + 1
b = f 0
c = let d = 1
e = 1
in d + e
print (a,b,c)
支持 let
的一个论点是它在 do
块中更突出,否则可能会填充各种类似单子赋值的运算符(想想用 operators defined in lens
).
do
p1.x += delta -- (+=) is a custom operator
p2.y -= delta -- (-=) is a custom operator
let delta' = delta*delta
p3 .= Point delta' delta' -- (.=) is a custom operator
由于 let
.
delta'
的语法糖与实际的 monadic 代码区分开来
我不知道为什么这样做,但这是我可以想象的一个原因:它允许您指定应按顺序建立哪些绑定,以及同时建立哪些绑定,这在阴影的情况下可能很重要。
例如,假设你的建议得到了实施,然后考虑:
foo :: [Int]
foo = do
x <- return [1]
y = 0:x
x = [1..5]
y
有两种合理的脱糖方法:
foo1 :: [Int]
foo1 = do
x <- return [1]
let y = 0:x
let x = [1..5]
y
foo2 :: [Int]
foo2 = do
x <- return [1]
let y = 0:x
x = [1..5]
y
foo1
的计算结果为 [0,1]
,foo2
的计算结果为 [0,1,2,3,4,5]
。当然,这是一种编写代码的奇怪方式,但 let
必须明确这一事实意味着您的意图没有歧义。
正如 let
绑定如何分组的唯一原因:函数定义可能需要多个方程式,以匹配多个参数模式。