haskellsyntax

Haskell requiring semicolon to be on a new line


I am new to haskell and had an university homework task that only compiled having a line consisting only of a single semicolon. I am curious, whether this is intended behaviour and if yes, why?

(I have produced interesting code lines over the last days that were not working as expected, each one was a small lecture, e.g. --| was not a commented out guard but an undefined operator, etc.)

The only version I got to work is (full code below):

   case input of {
       ... -- some other code
       ('0':xs)     -> [(next Zero, rest)] where (next, rest) = readAll xs
       ;
       ... -- some other code
   }

I wonder if it is possible to do it without a line consisting of a single semicolon and why the examples below did not work.

I tried e.g.

       ('0':xs)     -> [(next Zero, rest)] where (next, rest) = readAll xs;
       ('0':xs)     -> [(next Zero, rest)] where (next, rest) = (readAll xs);
       ('0':xs)     -> [(next Zero, rest)]
          where
             (next, rest) = (readAll xs);

but all of them led to the same kind of error:

file.hs:19:8: error: parse error on input ‘(’
   |
19 |        ('1':xs)     -> [(next One , rest)] where (next, rest) = readAll xs
   |        ^

A runnable code snippet:

data BinaryDigit = Zero | One
data BinaryNumber = L BinaryDigit -- L: last digit
                  | E BinaryDigit BinaryNumber -- E: intermediate digit

instance Show BinaryNumber where
   show (L Zero) = "0"
   show (L One ) = "1"
   show (E digit next) = show (L digit) ++ show next

instance Read BinaryNumber where
     -- idea:
     --   next item must be known to decide whether E or L constructor to use
     --   create function that will be called on the next element and returns the right constructor,
     --   with only one parameter, the current digit value, unset
   readsPrec _ input = case input of {
       ('0':'0':xs) -> readsPrec 0 ('0':xs);
       ('0':'1':xs) -> readsPrec 0 ('1':xs);
       ('0':xs)     -> [(next Zero, rest)] where (next, rest) = readAll xs
       ;
       ('1':xs)     -> [(next One , rest)] where (next, rest) = readAll xs
       ;
       otherwise    -> [];
   } where
     readAll :: String -> (BinaryDigit -> BinaryNumber, String)
     readAll ('0':xs) = ((`E` (next Zero)), rest) where (next, rest) = readAll xs
     readAll ('1':xs) = ((`E` (next One )), rest) where (next, rest) = readAll xs
     readAll str      = (L, str)

main = putStrLn $ show (read "110" :: BinaryNumber)

Solution

  • The reason you need a semicolon at all is that you disabled layout for the case expression by using curly braces on lines 15 and 23, so the alternatives must be explicitly separated by semicolons.

    If you add a semicolon after one of the expressions declared in the where clause, that semicolon is interpreted as separating the expressions declared inside that where clause. Therefore the semicolon must lie outside of that where's layout block. One solution is to wrap the where clause itself in curly braces:

       readsPrec _ input = case input of {
           ('0':'0':xs) -> readsPrec 0 ('0':xs);
           ('0':'1':xs) -> readsPrec 0 ('1':xs);
           ('0':xs)     -> [(next Zero, rest)] where {(next, rest) = readAll xs};
           ('1':xs)     -> [(next One , rest)] where {(next, rest) = readAll xs};
           otherwise    -> [];
       } where
    

    but idiomatic Haskell would take advantage of layout and avoid any semicolons:

       readsPrec _ input = case input of
           ('0':'0':xs) -> readsPrec 0 ('0':xs)
           ('0':'1':xs) -> readsPrec 0 ('1':xs)
           ('0':xs)     -> [(next Zero, rest)] where (next, rest) = readAll xs
           ('1':xs)     -> [(next One , rest)] where (next, rest) = readAll xs
           otherwise    -> []
         where