简体   繁体   中英

Partial Function Application in Haskell

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 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...

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.

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

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

It looks like you want:

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

or alteratively

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM