3

I have trouble in understanding the following Applicative instance. Can someone explain me what Applicative do(in this case) and how it can be used? Or write it less obfuscated? Thanks!

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

instance Applicative Parser where
    pure = success

    P p <*> P p' = P $ \s -> case p s of
        Just (f, s') -> fmap (applyToFirst f) $ p' s'
        Nothing      -> Nothing

{-|
    Applies a function to the first component of a pair.
-}
applyToFirst :: (a -> b) -> (a, c) -> (b, c)
applyToFirst f (x, y) = (f x, y)
yonutix
  • 1,964
  • 1
  • 22
  • 51
  • possible duplicate of [Getting started with Haskell](http://stackoverflow.com/questions/1012573/getting-started-with-haskell) – Bartek Banachewicz Nov 10 '14 at 11:00
  • What's "obfuscated" in the code you wrote? It's pretty clear. – Bartek Banachewicz Nov 10 '14 at 11:00
  • 4
    @BartekBanachewicz: I don't understand how it's a duplicate. – Tom Ellis Nov 10 '14 at 11:01
  • @TomEllis I hardly see value in explaining every single possible instance of `Applicative` on SO. – Bartek Banachewicz Nov 10 '14 at 11:01
  • [`DerivingVia`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/deriving_via.html?highlight=derivingvia#extension-DerivingVia): `newtype Parser a = P { getParser :: String -> Maybe (a, String) } deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadFix, MonadFail, MonadThrow, MonadState String)` – Iceland_jack Jul 16 '22 at 16:48
  • Try to understand `Applicative` instances in terms of the `liftA2` method, rather than `(<*>) = liftA2 ($)`. – Iceland_jack Jul 16 '22 at 16:49

2 Answers2

4

Maybe the following equivalent code makes it more clear what's going on?

instance Applicative Parser where
    pure v = P (\s -> Just (v, s))

    P p <*> P p' = P $ \s -> case p s of
        Just (f, s') -> case p' s' of
          Just (v, s'') -> Just (f v, s'')
          Nothing -> Nothing
        Nothing      -> Nothing
Dominique Devriese
  • 2,998
  • 1
  • 15
  • 21
  • Indeed, the `fmap` and `applyToFirst` do not made the code clearer than your expanded version, especially because `fmap` is used on another functor. – chi Nov 10 '14 at 17:31
1

Combining two parsers with <*> gives you new parser. Given an input string, the new parser runs the first parser returning a result and the unparsed remainder of the string. The remainder of the string is given to the second parser returning a result and unparsed remainder. Then the two results are combined. If either parser fails the result is failure.

Tom Ellis
  • 9,224
  • 1
  • 29
  • 54