Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell throws a parse error in a strange place

A toy example but still frustrating:

numberMapper:: IO ()
numberMapper = do codes <- forM [1 .. 4] (\num ->
                   do putStrLn $ "Enter a code for " ++ show num
                       code <- getLine
                       return code)
                   let numberCodes = zip [1 .. 4] codes
                   in forM numberCodes (\(num,code) ->
                   putStrLn $ "Got code " ++ show code ++ " for " ++ show num)

ghci tells me I have a Parse error in pattern: putStrLn and I can't figure out why it should fail to parse.

like image 429
agam Avatar asked Apr 10 '12 08:04

agam


Video Answer


2 Answers

Correction:

numberMapper:: IO ()
numberMapper = do
    codes <- forM [1 .. 4] $ \num -> do
        putStrLn $ "Enter a code for " ++ show num
        getLine
    let numberCodes = zip [1 .. 4] codes
    forM_ numberCodes $ \(num,code) ->
        putStrLn $ "Got code " ++ show code ++ " for " ++ show num

Fix: The lines inside a do block should line up.

-- wrong
a = do codes <- something
        let numberCodes = zip [1..4] codes

-- right
a = do codes <- something
       let numberCodes = zip [1..4] codes

Fix 2: When using let inside a do block, don't use in.

-- wrong
func = do
    let x = 17
    in print x

-- right
func = do
    let x = 17
    print x

Fix 3: Use forM_ (which returns (), a.k.a. void) instead of forM (which returns a list).

codes <- forM [1..4] func...  -- returns a list
forM_ numberCodes $ ...       -- discards list, returns () 

So forM_ could (almost) be written like this:

forM_ xs f = do forM xs f
                return ()

Minor change: You don't need return here:

do func1
   x <- func2
   return x

You can change it to the equivalent,

do func1
   func2 -- value of func2 is returned
like image 130
Dietrich Epp Avatar answered Nov 03 '22 00:11

Dietrich Epp


You over-indent lines in your do-blocks. Furthermore, you don't need an in for let statements in do-blocks.

This works for me:

numberMapper:: IO ()
numberMapper = do codes <- forM [1 .. 4] (\num ->
                   do putStrLn $ "Enter a code for " ++ show num
                      code <- getLine
                      return code)
                  let numberCodes = zip [1 .. 4] codes
                  forM numberCodes (\(num,code) ->
                    putStrLn $ "Got code " ++ show code ++ " for " ++ show num)

You can also structure it like this:

numberMapper:: IO ()
numberMapper = do codes <- forM [1 .. 4] $ \num ->
                   do putStrLn $ "Enter a code for " ++ show num
                      code <- getLine
                      return code
                  let numberCodes = zip [1 .. 4] codes
                  forM numberCodes $ \(num,code) ->
                    putStrLn $ "Got code " ++ show code ++ " for " ++ show num

(which lets you avoid the parentheses; alternatively, put the do at the end of the \num -> and line up subsequent statements)

like image 35
ivanm Avatar answered Nov 02 '22 23:11

ivanm