在 GHC 8.6.5 中是否删除了在 do 块中使用 let 语句的能力?

Has the ability to use let statements in do blocks been removed in GHC 8.6.5?

我在ghci中输入了一些代码,类似于:

main = do { a <- getLine ; let b = "Hello " ++ a ; putStrLn b }

但是,我得到这个错误:

<interactive>:1:63: error: parse error on input `}'

在 Haskell/GHC 的早期版本中,我记得这个工作得很好——甚至明确地说,在 do 块中,你不需要 in 关键字。然而,使它起作用的唯一方法似乎是:

main = do { a <- getLine ; let b = "Hello " ++ a in putStrLn b }

不会产生此错误。

这个被删除了吗?如果是这样,我是否需要在 let in 表达式中添加第二个 do 块?

这里的问题是它也将您的 putStrLn b 解析为 let 声明,因此它基本上将其解析为:

do { a <- getLine; let <b>{ b = "Hello " ++ a ; putStrLn b }</b> }

因此它正在 putStrLn 部分中寻找 =,您将在其中定义 putStrLn 函数。因此,解析器具有 "idea" 您正在定义一个函数,而不是调用一个函数。

的确,我们可以这样写例如:

Prelude> let a = 3; f b = b + 1
Prelude> f a
4

所以这里我们在同一行声明了两个变量。

您可以使用大括号来明确 let 的范围仅限于 b,例如:

do { a <- getLine; let <b>{ b = "Hello " ++ a }</b>; putStrLn b }

let 的优先级取决于 Chapter 3: Expressions in the Haskell'10 report 中定义的语法。

let 是一个类似于 do 的布局关键字,既作为 do 块中的语句,又作为 letin… 表达式中的语句,因为它引入了 绑定。这个:

main = do
  a <- getLine
  let b = "Hello " ++ a
  putStrLn b

对此进行脱糖:

main = do {
  a <- getLine;
  let {
    b = "Hello " ++ a;
  };
  putStrLn b;
};

而您所写的等同于:

main = do {
  a <- getLine;
  let {
    b = "Hello " ++ a;
    putStrLn b
  };
};

所以 GHC 自然会在 putStrLn b 之后期待其他东西——模式或 =,因为你 可以 定义一个名为 putStrLn 带有一个名为 b 的参数。解决方案是在 let 语句中使用显式大括号:

main = do { a <- getLine; let { b = "Hello " ++ a }; putStrLn b }

或者在 GHCi 中使用多行模式,要么使用 :{ 命令,要么使用 :} 命令终止:

> :{
| main = do
|   a <- getLine
|   let b = "Hello " ++ a
|   putStrLn b
| :}
>

或用:set +m,并以空行结束:

> :set +m
| main = do
|   a <- getLine
|   let b = "Hello " ++ a
|   putStrLn b
|
>

依次为:unset +m到return到single-line模式。