I'm working through the Brent Yorgey Haskell course, and I'm having trouble defining a good instance for Applicative. A parser is defined as follows:
newtype Parser a = Parser { runParser :: String -> Maybe (a, String) }
The function takes a string, parses a certain amount of input, and returns a Maybe tuple where the first value is the type of the parser, and the rest is the unparsed remainder of the string. For example, this is a parser for positive integers:
posInt :: Parser Integer
posInt = Parser f
where
f xs
| null ns = Nothing
| otherwise = Just (read ns, rest)
where (ns, rest) = span isDigit xs
The assignment is to make an Applicative instance for Parser. We start with a Functor instance (which is relatively straight-forward, I think):
first :: (a -> b) -> (a,c) -> (b,c)
first f (a, c) = (f a, c)
instance Functor Parser where
fmap f p = Parser f'
where f' s = fmap (first f) $ (runParser p) s
And then I tried my hand with Applicative:
collapse (Just (Just a)) = Just a
collapse _ = Nothing
extract (Just a, Just b) = Just (a,b)
extract _ = Nothing
appliedFunc :: Parser (a->b) -> Parser a -> String -> Maybe (b, String)
appliedFunc p1 p2 str = extract (f <*> fmap fst result2, fmap snd result2)
where result1 = (runParser p1) str
f = fmap fst result1
result2 = collapse $ fmap (runParser p2) $ fmap snd result1
instance Applicative Parser where
pure a = Parser (\s -> Just (a, s))
p1 <*> p2 = Parser (appliedFunc p1 p2)
...yuck. So my question is, how can I make my Applicative instance cleaner and less difficult to read? I feel like there's an easy answer for this question, but I haven't been able to wrap my head around the types just yet.