Bifunctors

Functors are themselves morphisms in the category CatCat which is the category of categories. They map one category to another. This means we can sometimes think of them as functions from one category to another category. Especially in HaskHask.

This means that functors can take more than one argument, and here we will go over a bifunctor (or binary functor), which is a functor in two arguments. Functor has kind * \rightarrow * while bifunctor has kind * \rightarrow * \rightarrow *.

In parallel with the functor which maps objects to objects the bifunctor maps pairs of objects to another category. Given two categories CC and DD the bifunctor maps the objects cCc \in C and dDd \in D to an object eEe \in E. It's a mapping from a Cartesian product of categories C×DEC \times D \mapsto E. Each object is itself a category.

And because functors map morphisms as well the bifunctor maps a pair of morphisms, one from each category to the product category. This composition is satisfied as per the categorical rules (it is associative and has identity).

(f,g)(f,g)=(ff,gg)(f, g) \circ (f', g') = (f \circ f', g \circ g')

These morphisms though are an actual set, the hom-set for the given objects in the category.

Bifunctor typeclass in Haskell:

Product and coproduct are bifunctors

Product as a bifunctor

The product of two objects, the categorical product, is a bifunctor. If a product exists for any pair of objects then the mapping from those objects to the product is bifunctorial.

The pair constructor is the simplest product (as we found before), and here is it's Bifunctor instance:

The action of the bifunctor here is to make pairs of types:

Coproduct as a bifunctor

The EitherEither type is itself a bifunctor, here is it's instance implementation:

Algebraic Data Types are functorial

Since ADTs are constructed from simpler data types, and we know that product and coproduct are functorial, it then follows that ADTs are also functorial as long as the type itself is parameterized (which is one of the requirements for a type to be a functor, it has kind * \rightarrow *).

Constructors that have no dependency on the functors type parameter are equivalent to the ConstConst functor while those that simply encapsulate the type are equivalent to the IdentityIdentity functor. These are the primitives used in the product and coproduct to construct ADTs.

Now we can see types in a different light. MaybeMaybe is a coproduct type which is functorial, and is therefore isomorphic to EitherEither of the Const()Const () and IdentityIdentity functors:

This means that MaybeMaybe is the composition of the bifunctor EitherEither with two functors Const()Const () and IdentityIdentity. ConstConst is actually a bifunctor but here it is used partially applied.