2

Consider the following code snippet in Idris:

myList : List Int
myList = [
  1,
  2,
  3
]

The closing delimiter ] is on the same column as the declaration itself. I find this a quite natural way to want to format long, multi-line lists.

However, the equivalent snippet in Haskell fails to compile with a syntax error:

myList :: [Int]
myList = [
  1,
  2,
  3
]

>>  main.hs:9:1: error:
>>     parse error (possibly incorrect indentation or mismatched brackets)?
>>   |
>> 9 | ]
>>   | ^

And requires instead the the closing delimiter ] is placed on a column number strictly greater than where the expression is declared. Or at least, as far as I can garner, this seems to be what is going on.

Is there a reason Haskell doesn't like this syntax? I know there are some subtle interactions between the Haskell parser and lexer to enable Haskell's implementation of the offsides rule, so perhaps it has something to do with that.

1
  • 2
    Anything which is as indented as your myList = ... line is assumed to start a new equation (or type signature). Alternatively, such lines can be separated by ; as in x = [1,2,3] ; y = "hello". Your indentation is equivalent to writing x = [1,2,3 ; ] y = "hello", which triggers an error. Haskell could have been designed to make an exception when parentheses are unmatched, of course. Still, I don't know what would be the "best" option, both have their pros and cons. Commented Jun 13, 2021 at 17:48

1 Answer 1

13

Well, ultimately the answer is just “because the Haskell language standard demands it to be parsed this way”.

As to for some reasoning why this is a good idea, it's that indentation is the primary way code is structured, and parentheses/brackets only come in locally. I find this much more consequent than Python's attitude that indentation is kind of the primary structure, but for an expression to spread over multiple lines you actually need to wrap it in parentheses. (Not saying that these are the only two ways it could be done.)

Note that if you really want, you can always disable the indentation sensitivity completely, with something like

myList :: [Int]
myList = l where {
l = [
   1,
   2,
   3
]}

But I would not recommend it. The preferred style to write multiline lists is

myList
 = [ 1
   , 2
   , 3
   ]

or

myList = [ 1
         , 2
         , 3 ]

Again, I would argue that this leading-comma style is much preferrable to the trailing-comma one most programmers in other languages use, especially for nested lists: the commas become “bullet points” aligned with the opening bracket, which makes the AST structure very clear.

myMonstrosity :: [(Int, [([Int], Int)])]
 = [ ( 1
     , [ ( [37,43]
         , 9 )
       , ( [768,4,9807,3,4,98]
         , 15 ) ]
     )
   , ( 2, [] )
   , ( 3
     , [ ( [], 300 )
       , ( [0..4000], -5 ) ]
     )
   ]
Sign up to request clarification or add additional context in comments.

1 Comment

I think the main reason people want this is because it plays nicer with Git. Many languages now support trailing comma, such as Swift, Kotlin, Rust, etc. This isn't a good enough reason for Haskell to do it, but it has some merit. However, Haskell uses less commas in general than other languages, and your initial comma syntax should work pretty well in most cases with Git by not "dirtying" the line above it due to the necessity to add a comma.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.