
An applicative, in category theory, is a lax monoidal endofunctor with tensorial strength. Let be a monoidal category. A lax monoidal endofunctor is a functor together with two coherence maps:
-
(the unit morphism)
-
(a natural transformation)
such that the following diagrams commute:
\begin{xy} \xymatrix{ (FX\ \otimes\ FY)\ \otimes\ FZ \ar[r]^{\alpha} \ar[d]_{\phi_{X,Y}\ \otimes\ FZ} & FX\ \otimes\ (FY\ \otimes\ FZ) \ar[d]^{FX\ \otimes\ \phi_{Y,Z}} \\ F(X\ \otimes\ Y)\ \otimes\ FZ \ar[d]_{\phi_{X\ \otimes\ Y,Z}} & FX\ \otimes\ F(Y\ \otimes\ Z) \ar[d]^{\phi_{X,Y\ \otimes\ Z}} \\ F((X\ \otimes\ Y)\ \otimes\ Z) \ar[r]_{F_{\alpha}} & F(X\ \otimes\ (Y\ \otimes\ Z)) \\ } \end{xy}The different natural transformations, (associativity), (right identity), (left identity) are part of the monoidal structure on .
Applicative functors are a relatively new concept. They were first introduced in 2008 by Conor McBride and Ross Paterson in their paper Applicative programming with effects.[1] In functional programming every functor is an endofunctor and every functor applied to the monoidal category , with the tensor product replaced by cartesian product, inherently possesses a unique strength, resulting in every functor within being strong. In simpler terms, a strong lax monoidal functor is just a lax monoidal functor that also has the property of being a strong functor, and its strength coherently associates with the monoidal structure. When we apply this in the context of functors, this coherent association is automatically provided.[2]
Example
The Applicative typeclass in Haskell looks slightly different then our definition of a lax monidal functor. However there is another typeclass in Haskell called Monoidal that reflects our definition. Moreover, there is a equivalence between the two typeclasses Applicative and Monoidal. This parallels our previous demonstration of the interchangeability between bind
and >>=
, as discussed in my post on monads. Let me first introduce the typeclass Monoidal and then we show that this is equivalent to Applicative.
Haskell Definition of Monoidal (Interface)
class Functor f => Monoidal f where
unit :: f ()
(**) :: f a -> f b -> f (a, b)
Please note that fa -> fb -> f(a, b)
is actually the curried version of
(f a, f b) -> f (a, b)
curry :: ((a, b) -> c) -> a -> b -> c
curry f x y = f (x, y)
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry f p = f (fst p) (snd p)
Haskell comes with curry
and uncurry
as part of its standard library, which together form an isomorphism.
Hence we can also phrase Monoidal this way, and it aligns seamlessly with our categorical definition of a strong lax monoidal functor:
class Functor f => Monoidal f where
-- η : 1 -> F(1) (unit)
unit :: () -> f ()
-- ϕx,y : F(X) ⊗ F(Y) -> F(X ⊗ Y)
(**) :: (f a, f b) -> f (a, b)
We have the usual monoidal laws (pseudocode):
unit ** v == v == v ** unit -- Left and Right Identity
u ** (v ** w) == (u ** v) ** w -- Associativity
Now that we have established the definition of Monoidal lets have a look at the equivalent Applicative definition in Haskell.
Haskell Definition of Applicative (Interface)
class Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
This is how to recover Applicative in terms of Monoidal:
pure :: Monoidal f => a -> f a
pure x = fmap (const x) unit
(<*>) :: Monoidal f => f (a -> b) -> f a -> f b
f <*> g = fmap uncurry ($) (f ** g)
And this is the reverse direction, Monoidal in terms of Applicative:
unit :: Applicative f => f ()
unit = pure ()
(**) :: Applicative f => f a -> f b -> f (a, b)
f ** g = fmap (,) f <*> g
We’ve now formulated a two-way translation between Applicative and Monoidal, illustrating that they are isomorphic. This equality between Applicative and Monoidal can also be shown in a computer-checked proof in Coq. Now, lets have a look at some instances of Applicative.
An Instance of Applicative, the List Applicative
instance Functor [] where
-- pure :: a -> [a]
pure x = [x]
-- (<*>) :: [(a -> b)] -> [a] -> [b]
fs <*> xs = [f x | f <- fs, x <- xs]
-- Thats a list comprehension that generates a new list
-- by applying the function f to each element in the list
-- xs for every function f in the list fs.
Another Instance, the Maybe Functor
instance Applicative Maybe where
-- pure :: a -> Maybe a
pure x = Just (x)
-- (<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
(Just f) <*> (Just x) = Just (f x)
_ <*> _ = Nothing
All of the above is already implemented in the standard Haskell library, so you can also simply open an interactive Haskell interpreter (ghci) and test the following examples.
ghci> [(*2)] <*> [1,2,3]
[2,4,6]
ghci> [(+1),(*2)] <*> [1,2,3]
[2,3,4,2,4,6]
ghci> Just (*2) <*> Just (3)
6
ghci> Just (*2) <*> Nothing
Nothing
References
- 0.The diagram displayed at the top of this post is a modified version of Brent Yorgey's Typeclassopedia diagram ↩
- 1.McBride, Conor; Paterson, Ross (2008-01-01). Applicative programming with effects (pdf). Journal of Functional Programming. 18 (1): 1–13. doi:10.1017/S0956796807006326 ↩
- 2.Rivas, E., & Jaskelioff, M. (2014). Notions of Computation as Monoids. CoRR, abs/1406.4823. https://arxiv.org/abs/1406.4823 ↩