Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 939
dbyrne Avatar asked Mar 16 '11 02:03

dbyrne


2 Answers

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)
like image 50
Edward Z. Yang Avatar answered Sep 29 '22 00:09

Edward Z. Yang


In your case, bind has type:

(>>=) :: Parser a -> (a -> Parser b) -> Parser b

(since you're using Parser as the Monad)

You give bind two arguments: the first one, many1 digit, should be ok (regarding the type); but the type of the second argument is the result type of liftM, namely Parser a -> Parser b and this does not fit the second argument's expected type (a -> Parser b)!

Without having tested it: instead of using liftM (Number.read) as second argument of bind, try using return . Number . read - this should have the right type and gives probably what you want...

like image 28
phynfo Avatar answered Sep 29 '22 00:09

phynfo