Typeclassopedia presents the following exercise:
Implement pure and (<*>) in terms of unit and (**), and vice versa.
Here\'s
Applicative
is a neat alternative presentation of Monoidal
. Both typeclasses are equivalent, and you can convert between the two without considering a specific data type like Option
. The "neat alternative presentation" for Applicative
is based on the following two equivalencies
pure a = fmap (const a) unit
unit = pure ()
ff <*> fa = fmap (\(f,a) -> f a) $ ff ** fa
fa ** fb = pure (,) <*> fa <*> fb
The trick to get this "neat alternative presentation" for Applicative
is the same as the trick for zipWith
- replace explicit types and constructors in the interface with things that the type or constructor can be passed into to recover what the original interface was.
unit :: f ()
is replaced with pure
which we can substitute the type ()
and the constructor () :: ()
into to recover unit
.
pure :: a -> f a
pure () :: f ()
And similarly (though not as straightforward) for substituting the type (a,b)
and the constructor (,) :: a -> b -> (a,b)
into liftA2
to recover **
.
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 (,) :: f a -> f b -> f (a,b)
Applicative
then gets the nice <*>
operator by lifting function application ($) :: (a -> b) -> a -> b
into the functor.
(<*>) :: f (a -> b) -> f a -> f b
(<*>) = liftA2 ($)
Getting from <*>
back to liftA2
is common enough that liftA2 is included in Control.Applicative. The <$>
is infix fmap
.
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = f <$> a <*> b