2

I'm just starting to learn Haskell from this wikibook and I had a little trouble with one of the exercises.

Specifically, the following doesn't work as I expect

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

unless I change it slightly

parseNumber :: Parser LispVal
parseNumber = (many1 digit) >>= (\n -> return $ Number . read $ n)

I was hoping someone could explain why return $ Number . read doesn't evaluate to the same lambda function that I explicitly created in the second definition, since I thought that this is exactly what partial function evaluation does when it's used in point free style code (obviously not!)

Thanks for any help, hopefully it's not another beginner’s monad problem...

1
  • 1
    return . Number . read should work, though. Commented Dec 21, 2013 at 13:19

3 Answers 3

4

This is just an issue of how $ associates. Fundamentally, $ is just an operator for writing fewer parentheses; it's the same as adding parentheses wrapping to the end of the expression.

Using this idea, we can rewrite your second example:

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

For reference, the original expression with parentheses looks like this:

parseNumber = (many1 digit) >>= (return (Number . read))

So the equivalent of the partial application is actually:

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

Basically, combining multiple $ associates differently than what you expected.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the answer, though I kinda understand how $ associates - I just imagined it left some sort of hole/place-holder at the end, seems stupid now. When I imagine this in scheme (if it had partial application) I would consider ((+) 1 2) ok though, since + applied to no argument is itself so you get (+ 1 2) but of course the parentheses really mean +(1, 2) so that's probably where I was going wrong
3

Go to the definitions --

($) :: (a -> b) -> a -> b
($) = id

(.) :: (b -> c) -> (a -> b) -> (a -> c)
(.) f g x = f (g x)

Now you have

return $ Number . read = ($) return (Number . read) -- (.) has higher precedence
                       = return (Number . read)

and the monad you're in is the Parser monad, so this is trying to bind a parsed value to a function that returns a parser for another function (many layers of abstraction!)

Instead, what you want is

return . Number . read

which is equivalent to what you wrote, as you can see by doing

\n -> return $ Number . read $ n = \n -> return . Number . read $ n  -- definition of (.)
                                 = return . Number . read            -- eta reduction

Finally, note that when you see the pattern

x >>= return . f

this can always be replaced with

fmap f x -- or liftM f x

i.e. it shows that you're not really using the Monad instance at all, but instead the weaker (and more general) Functor instance.

1 Comment

A thorough answer, thanks but you didn't quite get there the quickest. Like I explained on the accepted answer, the problem was I imagined a gap was left the argument (but in a functional language this would just be stupid since return's only arguments could be functions) so the accepted answer was more helpful
1

It looks like you want:

parseNumber = (many1 digit) >>= (return . Number . read)

or alteratively

parseNumber = (many1 digit) `fmap` (Number . read)

Number . read is a function String -> LispVal so the type of return $ Number . read is Parser (String -> LispVal), while you need the function to have type String -> Parser LispVal

1 Comment

Thanks, this solved my problem but I don't think it answered my question the best (It's still just as helpful since I wasn't seeing the difference between Parser (a -> b) and a-> Parser b at all)

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.