1

As part of a homework assignment we are working on a parser in Haskell. We have this data type

newtype Parser a = Parser { parse :: String -> Maybe (a,String) } 

This is clear to me,our parser gets a string are return expression from type a and the remaining unparsed string.

But next we are having this functor definition :

  instance Functor Parser where
       fmap f p = Parser $ \s -> (\(a,c) -> (f a, c)) <$> parse p s

And I'm completely lost. It seems to me that parse p s gives the parsed expression from type a but I can't understand what this fmap is actually doing and why it makes sense.

Hope that someone can give me a clue.

Thanks!

Tomer
  • 1,159
  • 7
  • 15
  • One thing that makes the definition more confusing than it needs to be is the fact that the tuple stores the result in the first position. If it were in the second, then you could use the functor instance of the tuple itself, to get `fmap f p = Parser $ \s -> fmap f <$> parse p s`. – chepner Feb 14 '20 at 12:23
  • (That is, `<$>` digs into the `Maybe` value, and `fmap` digs into the tuple.) – chepner Feb 14 '20 at 12:38

1 Answers1

3

Given a parser p, fmap fn p will create a new parser which parses the same text as p but then applies fn to the output. For instance, if you have a parser parseOptionalNumber :: Parser (Maybe Int), you can turn it into parseNumber :: Parser Int by doing parseNumber = fmap fromJust parseOptionalNumber (although you wouldn’t want to do this, as fromJust is partial).

As for the implementation, it works like this:

  • The whole thing is wrapped in Parser $ \s -> …, which creates a new parser which executes given an input string s.
  • parse p s uses the function parse :: Parser a -> (String -> Maybe (a,String)) (from the definition of Parser a) to run the input parser p on the input string s, producing an output of type Maybe (a,String).
  • (<$>) is a synonym of the function fmap, but as an operator rather than a function. It maps the function on the left over the Maybe (a,String) output on the right. The function it maps is \(a,c) -> (f a, c), which runs the given function f over the output (a,c) from the given parser p.

Hopefully that makes sense; if it didn’t, you may find it more useful to rewrite fmap in a less compact form:

instance Functor Parser where
    fmap f inParser = Parser $ \inputStr ->
        case (parse inParser inputStr) of
            Nothing -> Nothing
            Just (result, leftoverString) -> Just (f result, leftoverString)
bradrn
  • 8,337
  • 2
  • 22
  • 51
  • 1
    (side note) i.e. ... `fmap f p = Parser (\s -> do { (r,q) <- parse p s ; return (f r, q) }) === Parser (\s -> [ (f r, q) | (r,q) <- parse p s ])`, the last one with Monad Comprehensions. [This also means that](https://stackoverflow.com/a/60223269/849891) `parse (fmap f p) s == [ (f r, q) | (r,q) <- parse p s ]`. – Will Ness Feb 14 '20 at 16:09