'Haskell parsing, how '<*' '*>' works to parse something between delimiters
I start discovering parsing in Haskell without any libraries. I created my own data type to implement a couple of type classes.
data Parser a = Parser {
runParser :: String -> Maybe (a, String)
}
instance Functor Parser where
fmap fct (Parser p) = Parser $ \str -> p str >>= \(a, str1) -> Just (fct a, str1)
instance Applicative Parser where
pure a = Parser $ \ s -> Just (a, s)
(Parser p1) <*> (Parser p2) = Parser $ \ str -> do
(f, str1) <- p1 str
(a, str2) <- p2 str1
Just (f a, str2)
instance Alternative Parser where
empty = Parser $ \str -> Nothing
(Parser p1) <|> (Parser p2) = Parser $ \str -> p1 str <|> p2 str
For each classes, I have implemented minimal functions. I understand how they work, but for Applicative I don't understand how <* and *> work. I understand that it's interesting if I want to parse something between delimiters for example
runParser (parseChar 'x' *> parseChar 'a' <* parseChar 'x') "xax"
> Just ('a',"")
I also understand, that it will return "what is in direction of the arrow", so in that case the expression between delimiters. But, what I don't understand is when my string is applied to the first parser, it return a Maybe with parsed data and the rest of the string. How the rest of the string is given to the next parser ?
Solution 1:[1]
Let’s look at how *> is defined:
(*>) :: f a -> f b -> f b
a1 *> a2 = (id <$ a1) <*> a2
(<$) :: a -> f b -> f a
(<$) = fmap . const
Expanding this definition for Parser gives
Parser p1 *> Parser p2
(id <$ Parser p1) <*> Parser p2
(fmap . const) id (Parser p1) <*> Parser p2
fmap (const id) (Parser p1) <*> (Parser p2)
-- using fmap definition:
Parser (\str -> p1 str >>= \(a, str1) -> Just (const id a, str1)) <*> Parser p2
Parser (\str -> p1 str >>= \(a, str1) -> Just (id, str1)) <*> Parser p2
-- using <*> definition:
Parser $ \str -> do
(f, str1) <- (\str -> p1 str >>= \(a, str1) -> Just (id, str1)) str
(a, str2) <- p2 str1
Just (f a, str2)
Parser $ \str -> do
(f, str1) <- p1 str >>= \(a, str1) -> Just (id, str1)
(a, str2) <- p2 str1
Just (f a, str2)
Parser $ \str -> do
(_, str1) <- p1 str
(a, str2) <- p2 str1
Just (id a, str2)
Parser $ \str -> do
(_, str1) <- p1 str
(a, str2) <- p2 str1
Just (a, str2)
Now for <*:
(<*) :: f a -> f b -> f a
(<*) = liftA2 const
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f x = (<*>) (fmap f x)
Expanding it:
Parser p1 <* Parser p2
liftA2 const (Parser p1) (Parser p2)
(<*>) (fmap const (Parser p1)) (Parser p2)
-- using fmap definition:
(<*>) (Parser $ \str -> p1 str >>= \(a, str1) -> Just (const a, str1)) (Parser p2)
-- using <*> definition:
Parser $ \str -> do
(f, str1) <- (\str -> p1 str >>= \(a, str1) -> Just (const a, str1)) str
(a, str2) <- p2 str1
Just (f a, str2)
Parser $ \str -> do
(f, str1) <- p1 str >>= \(a, str1) -> Just (const a, str1)
(a, str2) <- p2 str1
Just (f a, str2)
-- renaming a to b:
Parser $ \str -> do
(f, str1) <- p1 str >>= \(a, str1) -> Just (const a, str1)
(b, str2) <- p2 str1
Just (f b, str2)
Parser $ \str -> do
(a, str1) <- p1 str
(b, str2) <- p2 str1
Just (const a b, str2)
Parser $ \str -> do
(a, str1) <- p1 str
(_, str2) <- p2 str1
Just (a, str2)
How the rest of the string is given to the next parser ?
In your definition of <*> in (f, str1) <- p1 str; (a, str2) <- p2 str1.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | cherryblossom |
