为什么在do块中需要let关键字?

时间:2016-11-17 05:28:52

标签: haskell let

let阻止do阻止的原因是什么?

-- 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)

假设没有let的所有in必须后跟=(这是真的吗?)

编译器应该能够let =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)

2 个答案:

答案 0 :(得分:15)

我不知道为什么会这样做,但这是我可以想象的一个原因:它允许你指定应该按顺序建立哪些绑定,以及哪些绑定同时,这在阴影的情况下很重要。

例如,假设您的建议已实施,然后考虑:

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必须是显式的这一事实意味着对于您的意图没有歧义。

正如chi的评论中所述,阴影并不是您可能需要明确说明let绑定如何分组的唯一原因:函数定义可能需要多个方程,以匹配多个参数模式。

答案 1 :(得分:4)

支持let的一个论点是它在do块中更突出,否则可能会被各种类似monadic赋值的运算符填充(想想充满{{3}的东西}})。

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

由于delta',我可以很容易地将用于抽象let的语法糖与实际的monadic代码区分开来。