'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