Search code examples
haskellsyntaxmonadsletdo-notation

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?


Solution

  • 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.