Search code examples
haskelltypesmonadsparsec

Monadic type confusion


I am going through Write Yourself a Scheme in Haskell. Its a great tutorial, but I've run into a wall with one of the parsing exercises:

parseNumber :: Parser LispVal
parseNumber = liftM (Number . read) $ many1 digit

Rewrite parseNumber using:

  1. Do-notation
  2. explicit sequencing with the >>= operator

I had no problems with do-notation:

parseNumber :: Parser LispVal
parseNumber = do x <- many1 digit 
                 let y = read x
                 return $ Number y

For #2 I've tried a bunch of variations such as:

parseNumber :: Parser LispVal
parseNumber = (many1 digit) >>= (liftM (Number . read))

but I keep running into type errors. I have two questions.

  1. Why am I getting type errors? Am I misunderstanding the monadic bind operator?
  2. Why AREN'T I getting similar type errors with my do-notation solution?

I feel like I am missing a fundamental concept regarding types?


Solution

  • You're attempting a non-trivial transformation from do-notation to bind notation, I recommend doing it the "trivial" way, and then making it points-free.

    Recall:

     x <- m    === m >>= \x ->
     let x = e === let x = e in
    

    Then you have:

     parseNumber = many1 digit >>= \x ->
                   let y = read x in
                   return (Number y)
    

    (I've removed the $ to avoid precedence problems.)

    We can then convert this into:

     parseNumber = many1 digit >>= \x -> return (Number (read x))
                 = many1 digit >>= return . Number . read
    

    Now, if you want to use liftM, you need to stop using bind, since the lifted function expects a monadic value as its argument.

     parseNumber = liftM (Number . read) (many1 digit)