If there is one structure that permeates category theory and, by implication, the whole of mathematics, it’s the monoid. To study the evolution of this concept is to study the power of abstraction and the idea of getting more for less, which is at the core of mathematics. When I say “evolution” I don’t necessarily mean chronological development. I’m looking at a monoid as if it were a life form evolving through various eons of abstraction.
It’s an ambitious project and I’ll have to cover a lot of material. I’ll start slowly, with the definitions of magmas and monoids, but then I will accelerate. A lot of concepts will be introduced in one or two sentences, mainly to familiarize the reader with the notation. I’ll dwell a little on monoidal categories, then breeze through ends, coends, and profunctors. I’ll show you how monads, arrows, and applicative functors arise from monoids in various monoidal categories.
The Magmas of the Hadean Eon
Monoids evolved from more primitive life forms feeding on sets. So, before even touching upon monoids, let’s talk about cartesian products, relations, and functions. You take two sets
b (or, in the simplest case, two copies of the same set
a) and form pairs of elements. That gives you a set of pairs, a.k.a., the cartesian product
a×b. Any subset of such a cartesian product is called a relation. Two elements
y are in a relation if the pair
<x, y> is a member of that subset.
A function from
b is a special kind of relation, in which every element
x in the set
a has one and only one element
y in the set
b that’s related to it. (Sometimes this is called a total function, since it’s defined for all elements of
Even before there were monoids, there was magma. A magma is a set with a binary operation and nothing else. So, in particular, there is no assumption of associativity, and there is no unit. A binary operation is simply a function from the cartesian product of
a with itself back to
a × a -> a
It takes a pair of elements
<x, y>, both coming from the set
a, and maps it to an element of
It’s tempting to quote the Haskell definition of a magma:
class Magma a where (<>) :: a -> a -> a
but this definition is already tainted with some higher concepts like currying. An alternative would be:
class Magma a where (<>) :: (a, a) -> a
Here, we at least see a pair of elements that are being “multiplied.” But the pair type
(a, a) is also a higher-level concept. I’ll come back to it later.
Lack of associativity means that we cannot identify
x<>(y<>z). You have to keep the parentheses.
You might have heard of quaternions — their multiplication is associative. But not many people have heard of octonions, which are not associative. In fact Hamilton, who discovered quaternions, invented the word associative to disassociate himself from octonions, which are not.
If you’re familiar with continuous groups, you might know that Lie algebras are not associative.
Closer to home — most operations on floating-point numbers are not associative on modern computers because of rounding errors.
But, really, most interesting binary operations are associative. So out of the magma emerges a semigroup. In a semigroup you can drop parentheses. A non-trivial (that is, non-monoidal) example of a semigroup is the set of integers with
max binary operation. A maximum of three numbers is the same no matter in which order you pair them. But there is no integer that’s less or equal to any other integer, so this is not a monoid.
Monoids of the Archean Eon
But, really, most interesting binary operations are both associative and unital. There usually is a “do nothing” element with respect to most binary operations. So life as we know it begins with a monoid.
A monoid is a set with a binary operation that is associative, and with a special element called the unit
e that is neutral with respect to the binary operation. To be precise, these are the three monoid laws:
(x <> y) <> z = x <> (y <> z) e <> x = x x <> e = x
In Haskell, the traditional definition of a monoid uses
mempty for the unit and
mappend for the binary operation:
class Monoid a where mempty :: a mappend :: a -> a -> a
As with the magma, the definition of
mappend is curried. Equivalently, it could have been written as:
mappend :: (a, a) -> a
I’ll come back to this point later.
There are plenty of examples of monoids. Non-negative integers with addition, or positive integers with multiplication are the obvious ones. Strings with concatenation are interesting too, because concatenation is not commutative.
Just like pairs of elements from two sets
b organize themselves into a set
a×b, which is their cartesian product; functions between two sets organize themselves into a set — the set of functions from
b, which we sometimes write as
This organizing principle is characteristic of sets, where everything you can think of is a set. Except when it’s more than just a set — for instance when you try to organize all sets into one large collection. This collection, or “class,” is not itself a set. You can’t have a set of all sets, but you can have a category Set of “small” sets, which are sets that belong to a “universe.” In what follows, I will confine myself to a single universe in order to dodge questions from foundational mathematicians.
Let’s now pop one level up and look at cartesian product as an operation on sets. For any two sets
b, we can construct the set
a×b. If we view this as “multiplication” of sets, we can say that sets form a magma. But do they form a monoid? Not exactly! To begin with, cartesian product is not associative. We can see it in Haskell: the type
((a, b), c) is not the same as the type
(a, (b, c)). They are, however, isomorphic. There is an invertible function called the associator, from one type to the other:
alpha :: ((a, b), c) -> (a, (b, c)) alpha ((x, y), z) = (x, (y, z))
It’s just a repackaging of containers (such repackaging is, by the way, called a natural transformation).
For the unit of this “multiplication” we can pick the singleton set. In Haskell, this is the type called unit and it’s denoted by an empty pair of parentheses
(). Again, the unit laws are valid up to isomorphism. There are two such isomorphisms called left and right unitors:
lambda :: ((), a) -> a lambda ((), x) = x rho :: (a, ()) -> a rho (x, ()) -> x
We have just exposed monoidal structure in the category Set. Set is not strictly a monoid because monoidal laws are satisfied only up to isomorphism.
There is another monoidal structure in Set. Just like cartesian product resembles multiplication, there is an operation on sets that resembles addition. It’s called disjoint sum. In Haskell it’s embodied in the type
Either a b . Just like cartesian product, disjoint sum is associative up to isomorphism. The unit (or the “zero”) of this sum type is the empty set or, in Haskell, the
Void type — also up to isomorphism.
The Cambrian Explosion of Categories
The first rule of abstraction is, You do not talk about Fight Club. In the category Set, for instance, we are not supposed to admit that sets have elements. An object in Set is really a set, but you never talk about its elements. We still have functions between sets, but they become abstract morphisms, of which we only know how they compose.
Composition of functions is associative, and there is an identity function for every set, which serves as a unit of composition. We can write these rules compactly as:
(f ∘ g) ∘ h = f ∘ (g ∘ h) id ∘ f = f f ∘ id = f
These look exactly like monoid laws. So do functions form a monoid with respect to composition? Not quite, because you can’t compose any two functions. They must be composable, which means their endpoints have to match. In Haskell, we can compose
g ∘ f, only if:
f :: a -> b g :: b -> c
Also, there is no single identity function, but a whole family of functions
ida, one for each set
a. In Haskell, we call that a polymorphic function.
But notice what happens if we restrict ourselves to just a single object
a in Set. Every morphism from
a back to
a can be composed with any other such morphism (their endpoints always match). Moreover, we are guaranteed that among those so called endomorphisms there is one identity morphism
ida, which acts as a unit of composition.
Notice that I switched from the set/function nomenclature to the more general object/morphism naming convention of category theory. We can now forget about sets and functions and define an arbitrary category as a collection (a set in a given universe) of objects, and sets of morphisms that go between them. The only requirements are that any two composable morphisms compose, and that there is an identity morphism for every object. And that composition must be associative.
We can now forget about sets and define a monoid as a category that has only one object. The binary operation is just the composition of (endo-)morphisms. It works! We have defined a monoid without a set. Or have we?
No, we haven’t! We have just swept it under the rug — the rug being the set of morphisms. Yes, morphisms between any two objects form a set called the hom-set. In a category C, the hom-set between objects
b is denoted by
C(a, b). So we haven’t completely eliminated sets from the picture.
In the single object category M, we have only one hom-set
M(a, a). The elements of this set — and we are allowed to call them elements because it’s a set — are morphisms like
g. We can compose them, and we can call this composition “multiplication,” thus recovering our previous definition of the monoid as a set. We get associativity for free, and we have the identity morphism
ida serving as the unit.
It might seem at first that we haven’t made progress and, in fact, we might have made some things more complicated by forgetting the internal structure of objects. For instance, in the category Set, it’s no longer obvious what an empty set is. You can’t say it’s a set with no elements because of the Fight Club rule. Similarly with the singleton set. Fortunately, it turns out that both these sets can be uniquely described in terms of their interactions with other sets. By that I mean the kind of functions/morphisms that connect them to other objects in Set. These object-opaque definitions are called universal constructions. For instance, the empty set is the only set that has a unique morphism going from it to every other set. The advantage of this characterization is that it can now be applied to any category. One may ask this question in any category: Is there an object that has this property? If there is, we call it the initial object. The empty set is the initial object in Set. Similarly, a singleton set is the terminal object in Set (and it’s unique up to unique isomorphism).
A cartesian product of two sets can also be defined using a universal construction, one which doesn’t mention elements (or pairs of elements). And again, this construction may be used to define a (categorical) product in other categories. Of particular interest are categories where a product exists for every pair of objects (it does in Set).
In such categories there is actually an even better way of defining a product using an adjunction. But before we can get to adjunctions, let me summarize a few millions of years of evolution in a few terse paragraphs.
A functor is a mapping of categories that preserves their structure. It maps objects to objects and morphisms to morphisms. In Haskell we define a functor (really, an endofunctor) as a type constructor
f (a mapping from types to types) that can be lifted to functions that go between these types:
class Functor f where fmap :: (a -> b) -> (f a -> f b)
The mapping of morphisms must also preserve composition and identity. Functors may collapse multiple objects into one, and multiple morphisms into one, but they never break connections. You may also think of functors as embedding one category inside another.
Finally, functors can be composed in the obvious way, and there is an identity endofunctor that maps a category onto itself. It follows that categories (at least the small ones) form a category Cat in which functors serve as morphisms.
There may be many ways of embedding one category inside another, and it’s extremely useful to be able to compare such embeddings by defining mappings between them. If we have two functors
G between two categories C and D we define a natural transformation between these functors by picking a morphism between a pair
F a and
G a, for every
In Haskell, a natural transformation between two functors
g is a polymorphic function:
type Nat f g = forall a. f a -> g a
In general, natural transformations must obey additional naturality conditions, but in Haskell they come for free (due to parametricity).
Natural transformations may be composed, and there is an identity natural transformations from any functor to itself. It follows that functors between any two categories C and D form a category denoted by
[C, D], where natural transformations play the role of morphisms. A hom-set in such a category is a set of natural transformations between two functors
G denoted by
[C, D](F, G).
An invertible natural transformation is called a natural isomorphism. If two functors are naturally isomorphic they are essentially the same.
Arthropods and their Adjoints
Using a pair of functors that are the inverse of each other we may define equivalence of categories, but there is an even more useful concept of adjoint functors that compare the structures of two non-equivalent categories. The idea is that we have a “right” functor
R going from category C to D and a “left” functor
L going in the other direction, from D to C.
There are two possible compositions of these functors, both resulting in round trips or endofunctors. The categories would be equivalent if those endofunctors were naturally isomorphic to identity endofunctors. But for an adjunction, we impose weaker conditions. We require that there be two natural transformations (not necessarily isomorphisms):
η :: ID -> R ∘ L ε :: L ∘ R -> IC
The first transformation η is called the unit; and the second ε, the counit of the adjunction.
In a small category objects form sets, so it’s possible to form a cartesian product of two small categories C and D. Object in such a category C×D are pairs of objects
<c, d>, and morphisms are pairs of morphisms
After these preliminaries, we are ready to define the categorical product in C using an adjunction. We chose C×C as the left category. The left functor is the diagonal functor Δ that maps any object
c to a pair
<c, c> and any morphism
f to a pair of morphisms
<f, f>. Its right adjoint, if it exists, maps a pair of objects
<a, b> to their categorical product
Interestingly, the terminal object can also be defined using an adjunction. This time we chose, as the left category, a singleton category with one object and one (identity) morphism. The left functor maps any object
c to the singleton object. Its right adjoint, if it exists, maps the singleton object to the terminal object in C.
A category with all products and the terminal object is called a cartesian category, or cartesian monoidal category. Why monoidal? Because the operation of taking the categorical product is monoidal. It’s associative, up to isomorphism; and its unit is the terminal object.
Incidentally, this is the same monoidal structure that we’ve seen in Set, but now it’s generalized to the level of other categories. There was another monoidal structure in Set induced by the disjoint sum. Its categorical generalization is given by the coproduct, with the initial object playing the role of the unit.
But what about the set of morphisms? In Set, morphisms between two sets
b form a hom-set, which is the object of the same category Set. In an arbitrary category C, a hom-set
C(a, b) is still a set — but now it’s not an object of C. That’s why it’s called the external hom-set. However, there are categories in which each external hom-set has a corresponding object called the internal hom. This object is also called an exponential,
ba. It can be defined using an adjunction, but only if the category supports products. It’s an adjunction in which the left and right categories are the same. The left endofunctor takes an object
b and maps it to a product
a is an arbitrary fixed object. Its adjoint functor maps an object
b to the exponential
ba. The counit of this adjunction:
ε :: ba × a -> b
is the evaluation function. In Haskell it has the following signature:
eval :: (a -> b, a) -> b
The Haskell function type
a->b is equivalent to the exponential
A category that has all products and exponentials together with the terminal object is called cartesian closed. Cartesian closed categories, or CCCs, play an important role in the semantics of programming languages.
We have already seen two very similar monoidal structures induced by products and coproducts. In mathematics, two is a crowd, so let’s look for a pattern. Both product and coproduct act as bifunctors
C×C->C. Let’s call such a bifunctor a tensor product and write it as an infix operator
a ⊗ b. As a bifunctor, the tensor product can also lift pairs of morphisms:
f :: a -> a' g :: b -> b' f ⊗ g :: a ⊗ b -> a' ⊗ b'
To define a monoid on top of a tensor product, we will require that it be associative — up to isomorphism:
α :: (a ⊗ b) ⊗ c -> a ⊗ (b ⊗ c)
We also need a unit object, which we will call
i. The two unit laws are:
λ :: i ⊗ a -> a ρ :: a ⊗ i -> a
A category with a tensor product that satisfies the above properties, plus some additional coherence conditions, is called a monoidal category.
We can now specialize the tensor product to categorical product, in which case the unit object is the terminal object; or to coproduct, in which case we chose the initial object as the unit. But there is an even more interesting operation that has all the properties of the tensor product. I’m talking about functor composition.
Functors between any two categories C and D form a functor category
[C, D] with natural transformations playing the role of morphisms. In general, these functors don’t compose (their endpoints don’t match) unless we pick the target category to be the same as the source category.
In the endofunctor category
[C, C] any two functors can be composed. But in
[C, C] functors are objects, so functor composition becomes an operation on objects. For any two endofunctors
G it produces a new endofunctor
F∘G. It’s a binary operation, so it’s a potential candidate for a tensor product. Indeed, it is a bifunctor: it can be lifted to natural transformations, which are morphisms in
[C, C]. It’s associative — in fact it’s strictly associative, the associator α is the identity natural transformation. The unit with respect to endofunctor composition is the identity functor
I. So the category of endofunctors is a monoidal category.
Unlike product and coproduct, which are symmetric up to isomorphism, endofunctor composition is not symmetric. In general, there is no relation between
Different species of functors came up with their own composition strategies. Take for instance the profunctors, which are functors
Cop×D->Set. They generalize the idea of relations between objects in C and D. The sets they map to may be thought of as sets of proofs of the relationship. An empty set means that the two objects are not related. If you want to compose two relations, you have to find an element that’s common to the image of one relation and the source of the other (relations are not, in general, symmetric). The proofs of the new composite relation are pairs of proofs of individual relations. Symbolically, if
q are such profunctors/relations, their composition can be written as:
exists x. (p a x, q x b)
Existential quantification in Haskell translates to polymorphic construction, so the actual definition is:
data PCompose p q a b = forall x . PCompose (p a x) (q x b)
In category theory, existential quantification is encoded as the coend, which is a generalization of a colimit for profunctors. The coend formula for the composition of two profunctors reads:
(p ⊗ q) a b = ∫ z p a z × q z b
The product here is the cartesian product of sets.
Profunctors, being functors, form a category in which morphisms are natural transformations. As long as the two categories that they relate are the same, any two profunctors can be composed using a coend. So profunctor composition is a good candidate for a tensor product in such a category. It is indeed associative, up to isomorphism. But what’s the unit of profunctor composition? It turns out that the simplest profuctor — the hom-functor — because of the Yoneda lemma, is the unit of composition:
∫ z C(a, z) × p z b ≅ p a b ∫ z p a z × C(z, b) ≅ p a b
Cop×C->Set form a monoidal category.
Or consider Set-valued functors. They can be composed using Day convolution. For that, the category C must itself be monoidal. Day convolution of two functors
C->Set is defined using a coend:
(f ★ g) a = ∫ x y f x × g y × C(x ⊗ y, a)
Here, the tensor product of
x ⊗ y comes from the monoidal category C, the other products are just cartesian products of sets (one of them being the hom-set).
As before, in Haskell, the coend turns into existential quantifier, which can be written symbolically:
Day f g a = exists x y. ((f x, g y), (x, y) -> a)
and encoded as a polymorphic constructor:
data Day f g a = forall x y. Day (f x) (g y) ((x, y) -> a)
We use the fact that the category of Haskell types is monoidal with respect to cartesian product.
We can build a monoidal category based on Day convolution. The unit with respect to Day convolution is
C(i, -), the hom-functor applied to
i — the unit in the monoidal category C. For instance, the left identity can be derived from:
(C(i, -) ★ g) a = ∫ x y C(i, x) × g y × C(x ⊗ y, a)
Applying the Yoneda lemma, or “integrating over
x,” we get:
∫y g y × C(i ⊗ y, a)
i is the unit of the tensor product, we can perform the second integration to get
The Monozoic Era
Monoidal categories are important because they provide rich grazing grounds for monoids. In a monoidal category we can define a more general monoid. It’s an object
m with some special properties. These properties replace the usual definitions of multiplication and unit.
First, let’s reformulate the definition of a set-based monoid, taking into account the fact that Set is a monoidal category with respect to cartesian product.
A monoid is a set, so it’s an object in Set — let’s call it
m. Multiplication maps pairs of elements of
m back to
m. These pairs are just elements of the cartesian product
m × m. So multiplication is defined as a function:
μ :: m × m -> m
Unit of multiplication is a special element of
m. We can select this element by providing a special morphism from the singleton set to
η :: () -> m
We can now express associativity and unit laws as properties of these two functions. The beauty of this formulation is that it generalizes easily to any cartesian category — just replace functions with morphisms and the unit
() with the terminal object. There’s no reason to stop there: we can lift this definition all the way up to a monoidal category.
A monoid in a monoidal category is an object
m together with two morphisms:
μ :: m ⊗ m -> m η :: i -> m
i is the unit object with respect to the tensor product ⊗. Monoidal laws can be expressed using the associator α and the two unitors, λ and ρ, of the monoidal category:
Having previously defined several interesting monoidal categories, we can now go digging for new monoids.
Let’s start with the category of endofunctors where the tensor product is functor composition. A monoid in the category of endofunctors is an endofunctor
m and two morphism. Remember that morphisms in a functor category are natural transformations. So we end up with two natural transformations:
μ :: m ∘ m -> m η :: I -> m
I is the identity functor. Their components at an object
μa :: m (m a) -> m a ηa :: a -> m a
This construct is easily recognizable as a monad. The associativity and unit laws are just monad laws. In Haskell,
μa is called
ηa is called
Let’s switch to the category of profunctors
Cop×C->Set with profunctor composition as the tensor product. A monoid in that category is a profunctor
ar. Multiplication is defined by a natural transformation:
μ :: ar ⊗ ar -> ar
Its component at
μa b :: (∫ z ar a z × ar z b) -> ar a b
To simplify this formula we need a very useful identity that relates coends to ends. A hom-set that starts at a coend is equivalent to an end of the hom set:
C(∫ z p z z, y) ≅ ∫ z C(p z z, y)
Or, replacing external hom-sets with internal homs:
(∫ z p z z) -> y ≅ ∫ z (p z z -> y)
In Haskell, this formula is used to turn functions that take existential types to functions that are polymorphic:
(exists z. p z z) -> y ≅ forall z. (p z z -> y)
Intuitively, it makes perfect sense. If you want to define a function that takes an existential type, you have to be prepared to handle any type.
Using that identity, our multiplication formula can be rewritten as:
μa b :: ∫ z ((ar a z × ar z b) -> ar a b)
In Haskell, this derivation uses the existential quantifier:
mu a b = (exists z. (ar a z, ar z b)) -> ar a b
As we discussed, a function from an existential type is equivalent to a polymorphic function:
forall z. (ar a z, ar z b) -> ar a b
or, after currying and dropping the redundant quantification:
ar a z -> ar z b -> ar a b
This looks very much like a composition of morphisms in a category. In Haskell, this function is known in the infix-operator form as:
(>>>) :: ar a z -> ar z b -> ar a b
Let’s see what we get as the monoidal unit. Remember that the unit object in the profunctor category is the hom-functor
ηa b :: C(a, b) -> ar a b
In Haskell, this polymorphic function is traditionally called
arr :: (a -> b) -> ar a b
The whole construct is known in Haskell as a pre-arrow. The full arrow is defined as a monoid in the category of strong profunctors, with strength defined as a natural transformation:
sta b :: p a b -> p (a, x) (b, x)
In Haskell, this function is called
There are several categorical formulations of what’s called in Haskell the applicative functor. To first approximaton, Haskell’s type system is the category Set. To translate Haskell constructs to category theory, the safest approach is to just play with endofunctors in Set. But both Set and its endofunctors have a lot of extra structure, so I’d like to start in a slightly more general setting.
Let’s have a look at the monoidal category of functors
[C, Set], with Day convolution as the tensor product, and
C(i, -) as unit. A monoid in this category is a functor
f with multiplication given by the natural transformation:
μ :: f ★ f -> f
and unit given by:
η :: C(i, -) -> f
It turns out that the existence of these two natural transformations is equivalent to the requirement that
f be a lax monoidal functor, which is the basis of the definition of the applicative functor in Haskell.
A monoidal functor is a functor that maps monoidal structure of one category to the monoidal structure of another category. It maps the tensor product, and it maps the unit object. In our case, the source category C has the monoidal structure given by the tensor product ⊗, and the target category Set is monoidal with respect to the cartesian product ×. A functor is monoidal if it doesn’t matter whether we first map two object and then multiply them, or first multiply them and then map the result:
f x × f y ≅ f (x ⊗ y)
Also, the unit object in Set should be isomporphic to the result of mapping the unit object in C:
() ≅ f i
() is the terminal object in Set and
i is the unit object in C.
These conditions are relaxed in the definition of a lax monoidal functor. A lax monoidal functor replaces isomorphisms with regular unidirectional morphisms:
f x × f y -> f (x ⊗ y) () -> f i
It can be shown that the monoid in the category
[C, Set], with Day convolution as the tensor product, is equivalent to the lax monoidal functor.
The Haskell definition of
Applicative doesn’t look like Day convolution or like a lax monoidal functor:
class Functor f => Applicative f where (<*>) :: f (a -> b) -> (f a -> f b) pure :: a -> f a
You may recognize
pure as a component of η, the natural transformation defining the monoid with respect to Day convolution. When you replace the category C with Set, the unit object
C(i, -) turns into the identity functor. However, the operator
<*> is lifted from the definition of yet another lax functor, the lax closed functor. It’s a functor that preserves the closed structure defined by the internal hom functor. In Set, the internal hom functor is just the arrow
(->), hence the definition:
class Functor f => Closed f where (<*>) :: f (a -> b) -> (f a -> f b) unit :: f ()
As long as the internal hom is defined through the adjunction with the product, a lax closed functor is equivalent to a lax monoidal functor.
It is pretty shocking to realize how many different animals share the same body plan — I’m talking here about the monoid as the skeleton of a myriad of different mathematical and programming constructs. And I haven’t even touched on the whole kingdom of enriched categories, where monoidal categories form the reservoir of hom-objects. Virtually all notions I’ve discussed here can be generalized to enriched categories, including functors, profunctors, the Yoneda lemma, Day convolution, and so on.
- Hadean Eon: Began with the formation of the Earth about 4.6 billion years ago. It’s the period before the earliest-known rocks.
- Archean Eon: During the Archean, the Earth’s crust had cooled enough to allow the formation of continents.
- Cambrian explosion: Relatively short evolutionary event, during which most major animal phyla appeared.
- Arthropods: from Greek ἄρθρωσις árthrosis, “joint”
- Tensor, from Latin tendere “to stretch”
- Functor: from Latin fungi, “perform”