Friday, November 15, 2013

Applicative Functors

Well, perhaps it's time to resurrect this blog from benign neglect. I must admit that I've been spending close to zero time thinking about comonads and coalgebras lately. This is partly a consequence of having a Real Job, and partly a consequence of the fact that the remainder of my time apart from this is devoted to homotopy type theory and whiskey.

Herein, I simply wish to share a picture of what applicative functors are in category theoretic language.

To this end, let $F: \mathcal{C} \rightarrow \mathcal{D}$ be a functor between cartesian closed categories. We shall call $F$ applicative if there exists a natural transformation of bifunctors $\alpha: F(-^{-}) \rightarrow F(-)^{F(-)}$ (the blanks are different here - is that confusing?). In other words, an applicative functor doesn't necessarily "preserve" exponentials, but there is a map.

In Haskell, $\alpha$ is typically denoted <*>

Also, recall that functor application is actually a function fmap :: Functor f => (a -> b) -> f a -> f b

(I don't want to terminate these sentences with periods because I don't want to gum up the syntax in some way. How do people usually deal with this?)

Now, as fmap can be rather cumbersome to write, I've seen <$> used instead. Well, that makes plenty of sense actually: the operator $ is a special case of this with respect to the identity functor.

Now, a common use of this is to deal with functions that take multiple arguments "inside" of functors. Remember that everything in Haskell is curried by default, so, for instance, addition has the following type:

(+) :: Num a => a -> a -> a


(<$>) (+) :: (Functor f, Num a) => f a -> f (a -> a)

So if we compose that with <*>, we get:

(<*>) . ((<$>) (+)) :: (Num a, Control.Applicative.Applicative f) => f a -> f a -> f a

So, we can now perform addition "inside" any applicative functor, so if you evaluate

(((<*>) . ((<$>) (+))) (Just 1)) (Just 2)

you get

Just 3

Of course, what I wrote above is really ugly, so in practice we would actually use the infix form:

(+) <$> Just 1 <*> Just 2

Now, if you were going to do something like this often, you might as well define a new operator:

let (<+>) x y = (+) <$> x <*> y

So that

Just 1 <+> Just 2

evaluates to

Just 3

Of course, the example functor I am using is rather special, as it creates an "extra value" for any given type. You might wonder what happens when I add nothing to just a number:

Just 1 <+> Nothing

Well, it evaluates to:


Well, after all, Maybe is a functor from types to pointed types, so I think it makes perfect sense that our natural transformation preserves the new "basepoint" which we call Nothing.

No comments:

Post a Comment