Question

I've been reading up on Applicative Functors and I am having difficulty reconciling a mismatch in the respective terminologies of category theory and functional programming.

Although I have looked over various blogs, the most complete resources that I have been using for this research are:

In category theory, a functor is a morphism from a source category to a target category (in a category of categories). The "category of categories" has a collection of objects that contains both the source and target categories and a collection of functors that contains: the source category's identity functor; the target category's identity functor; and, the functor connecting the source to the target (If the source category is the same as the target category and the functor in question is the identity, then there need only be that one functor).

In functional programming, applicative functors are described as a pair of operations:

  • pure : a -> f a
  • <*> : f ( a -> b) -> f a -> f b.

Here's my question

What interpretation makes clear the correspondence between the the functional programming definition of an applicative functor and the category theoretical definition of a functor?

More specifically, what parts of the tuple (pure,<*>) correspond to:

  1. the source category
  2. the target category
  3. the category where the functor lives
  4. the functor's structure-preserving effect on the objects of the source category
  5. the functor's structure-preserving effect on the morphisms of the source category

Notes: I recognize that this may be an incomplete metaphor, and there may not be a one-to-one correspondence for each of the concepts that I mentioned. I have purposely refrained from sharing my speculation about the apparent correspondences here in order to keep my question simple and to avoid confounding the issues further.

Was it helpful?

Solution

Paraphrasing this answer: Applicative functors are functors for which there is also a natural transformation that preserve monoidal structure of their source/target categories. In the case of Haskell's Applicative endofunctors (because their source and target categories is Hask), the monoidal structure is the Cartesian product. So for an Applicative functor there are natural transformations φ: (f a, f b) -> f (a, b) and ι: () -> f (). So we could define Applicative as

class Functor f => Applicative' f where
    φ :: (f a, f b) -> f (a, b)
    ι :: f ()       -- it could be \() -> f (),
                    -- but \() -> ... is redundant

This definition is equivalent to the standard one. We can express

φ = uncurry (liftA2 (,))
  = \(x, y) -> (,) <$> x <*> y

ι = pure ()

and conversely

pure x   = fmap (\() -> x) ι

f <*> x  = fmap (uncurry ($)) (φ (f, x))

So pure and <*> is an alternative way how to define this natural transformation.

OTHER TIPS

It's probably simpler to look at the class Functor first (which is a superclass of Applicative). Applicative corresponds to a "lax monoidal functor", as the first paper you linked to points out. The definition of Functor is:

class Functor f where
  fmap :: (a -> b) -> f a -> f b

An instance of Functor is a type constructor (of kind * -> *). An example is Maybe.

The category we're talking about is the category "Hask" that has Haskell types as objects and (monomorphic) Haskell functions as morphisms. Every instance of Functor (and Applicative, Monad, etc.) is an endofunctor in this category, i.e. a functor from the category to itself.

The two maps of the functor -- Haskell-types-to-Haskell-types and Haskell-functions-Haskell-functions -- are the type constructor f and the function fmap.

For example, Int and Maybe Int are both objects in Hask; Maybe maps from the former to the latter. If chr :: Int -> Char, then fmap maps it to fmap chr :: Maybe Int -> Maybe Char. The Functor laws correspond to the categorical functor laws.

In the case of Applicative, Functor is a superclass, so everything I just said applies. In this particular case you can implement fmap using pure and <*> -- liftA f x = pure f <*> x -- so the two parts of the functor that you were looking for are f and liftA. (But note that other formulations of Applicative don't let you do that -- in general you would rely on the Functor superclass.)

I'm not quite sure what you mean here by "the category where the functor lives".

The way you posed your question suggest that you are looking for a category of categories, whose morphisms can be understood as Haskell functors. That would be kinds then. The category has one object which is *, representing the category of haskel types. Since there only is one object, there only has to be one set of morphisms, which is type constructors (of kind * -> *). Not every type constructor ist a functor, but every functor is a type constructor. So in this sense it can be understood as a morphism from * to *.

The point of kinds is of course to keep track of the number of arguments to a type constructor. To understand this as a category is somehow artificial, since it only has one object.

Also, there is no real identity morphism for the object *. You could think of Identity :: * -> *, but it's not an identity in a strict sense (it is up to natural isomorphism though, since you have Identity :: forall a . a -> Identity a and runIdentity :: forall a . Identity a -> a). The same goes for composition: You always have to use an explicit isomorphism to deal with composed functors (Compose/ getCompose).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top