user4385532
user4385532

Reputation:

Does a case expression in a let expression require braces and semicolons?

I'm confused by Haskell's parsing rules.

This works beautifuly:

n = 5
m = 6
b = case (n, m) of
  (5, 6) -> True
  _ -> False

main = print b

Let's complicate it only a teeny tiny bit, let's add a let to the mixture:

b =
  let res = case (n, m) of
    (5, 6) -> True
    _ -> False
  in not res

(Note, for brevity I'm omitting definitions of n, m and main from now on; they carry on the same, I'm only changing b)

Oops, problems here:

wtf.hs:5:5: error: parse error on input ‘(’
Failed, modules loaded: none.

I don't know, maybe that's some kind of a weird indentation rule that I'm not getting. Fine, let's put explicit brackets:

b =
  let res = case (n, m) of {
    (5, 6) -> True
    _ -> False }
  in not res

Still not?!

wtf.hs:6:7: error: parse error on input ‘->’
Failed, modules loaded: none.

I'm totally confused. I don't know what to do. Why won't this work?? Let's add an explicit semicolon here, even though this is honestly a blind shot and even though I don't understand why would it be needed here, because after all, AFAIK, a newline (which is present here) should make semicolons redundant:

b =
  let res = case (n, m) of {
    (5, 6) -> True;
    _ -> False }
  in not res

And this finally works!

... don't know, maybe the problem lies in let and case being on the same line. As a last attempt to research this myself, let's try this:

b =
  let res =
    case (n, m) of
      (5, 6) -> True
      _ -> False
  in not res

This however won't work for reasons that are mysterious to me:

wtf.hs:5:5: error:
    parse error (possibly incorrect indentation or mismatched brackets)
Failed, modules loaded: none.

Seriously, I'm confused here. Why are explicit brackets and semicolons needed here? (And are they? Can the code be formatted in such a way that they are not needed?)

Which obscure parsing rule of Haskell I'm not getting here?

Upvotes: 6

Views: 724

Answers (1)

Mor A.
Mor A.

Reputation: 4646

From the Haskell 2010 Report:

Informally stated, the braces and semicolons are inserted as follows. The layout (or “off-side”) rule takes effect whenever the open brace is omitted after the keyword where, let, do, or of. When this happens, the indentation of the next lexeme (whether or not on a new line) is remembered and the omitted open brace is inserted (the whitespace preceding the lexeme may include comments). For each subsequent line, if it contains only whitespace or is indented more, then the previous item is continued (nothing is inserted); if it is indented the same amount, then a new item begins (a semicolon is inserted); and if it is indented less, then the layout list ends (a close brace is inserted)...

...Also, these rules permit:

f x = let a = 1; b = 2  
          g y = exp2  
       in exp1 

This example actually shows how you are expected to treat the indentation in Haskell, basically it's not the keywords that determine how indented something is, rather it is the first identifier (or other lexeme) following them, so in the case of

b = case (n, m) of
  (5, 6) -> True
  _ -> False

This is fine since the 2nd and 3rd lines are indented more than the b in the first line, on the other hand, the following

b =
  let res =
    case (n, m) of
      (5, 6) -> True
      _ -> False
  in not res

Is essentially parsed as

b =
  let { res =
    } case (n, m) of
     { (5, 6) -> True
     ; _ -> False
  } in not res  

And this is because case isn't indented more than res, so it's not part of its definition.
and it's why the compiler complains about a parse error (it expects a lexeme after = but gets nothing, it also doesn't expect the case there since it doesn't fit the let ... in ... syntax).

Instead you should write

b =
  let res =
        case (n, m) of
          (5, 6) -> True
          _ -> False
  in not res

or

b =
  let res = case (n, m) of
        (5, 6) -> True
        _ -> False
  in not res

Both of which will be parsed as you expect.

Upvotes: 11

Related Questions