简体   繁体   中英

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

I have entered some code in ghci , similar to this:

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

However, I get this error:

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

In previous versions of Haskell/GHC, I remember this working just fine - it was even explicitly said that, in do blocks, you don't need the in keyword. Yet, the only way to get this to work seems to be:

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

which doesn't produce this error.

Has this been removed? If so, do I need a second do block inside the let in expression?

let is a layout keyword like do , both as a statement in a do block and in a letin … expression, because it introduces a block of bindings. This:

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

Desugars to this:

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

Whereas what you've written is equivalent to this:

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

So naturally GHC is expecting something else—a pattern or = —after putStrLn b , since you could be defining a local function named putStrLn with a parameter named b . The solution is either to use explicit braces in the let statement:

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

Or to use multiline mode in GHCi, either with the :{ command, terminated with the :} command:

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

Or with :set +m , and terminated with a blank line:

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

Followed by :unset +m to return to single-line mode.

The problem is here that it parses your putStrLn b as a let declaration as well, so it parses it basically as:

do { a <- getLine; let  }

It thus is looking for a = in the putStrLn part, where you will define the putStrLn function. The parser thus has the "idea" that you are defining a function, not calling a function.

Indeed, we can write for example:

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

so here we declared two variables in the same line.

You can use curly brackets to make clear that the let is scoped to only b , like:

do { a <- getLine; let ; putStrLn b }

The priority of let is due to the grammar, defined in Chapter 3: Expressions in the Haskell'10 report .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM