So, the quote at the beginning is true, but with a few qualifications. As most beginning functional programmers know it, the monoid is a structure that has an identity element \(id\) and a binary operator \(\otimes\):

For the monoid \(m\) to be valid, the \(id\) must be the identity with respect to the operator \(\otimes\), like \(0\) is to \(+\) on natural numbers, or \(1\) to \(\times\), etc; the binary operator must also be associative:

In fact, \(\eta\) is a natural transformation1 from the Identity functor to another functor \(m\); \(\mu\) is a natural transformation from \(m^2\) to \(m\) (that is, from \(m\) applied twice to \(m\) applied once).

A Difference of Kinds

You’ll note that our \(\textbf{Monad}\) and \(\textbf{Monoid}\) operate in totally different worlds: a difference of kinds. That is, an instance of the former is a type of values (\(m :\star\)), whereas an instance of the latter is an arrow from one type to another (a type constructor, \(m :\star\to\star\)). By analogy, then, the former’s functions should be natural transformations in the latter.

This makes fine sense, but the question of what to do with \(id\) remains: why does it have an input in \(\textbf{Monad}\), but not in \(\textbf{Monoid}\)? If we are going to understand these functions as arrows between objects in a category, then \(id\) must have an input. As a natural transformation in \(\textbf{Monad}\), its input is the identity functor; as a simple function in \(\textbf{Monoid}\), its input should be \(\varnothing\), nothing.

If we uncurry \(\otimes\), then its type becomes (\(m\times m\to m\)); this adjustment brings its type in line with that of \(\mu\). So, we can build the following (incomplete and flawed) generalization over an identity element \(id\) and a type-operator \(\times\):

But this doesn’t work, since the type of \(id\) for our second (monadic) instance reduces to \(Id\to []\), which doesn’t type-check: that is, the kind of \((\to)\) is \(\star\to\star\to\star\), but the kind of each of its operands in \(id\) is already (\(\star\to\star\)). What this means is that for monadic instances of \(\textbf{Monoid}\), we need an arrow type constructor of kind \((\star\to\star)\to(\star\to\star)\to\star\). So we need to abstract over kind of arrow constructor:

We use functional dependencies to help the typechecker understand that m and ~> uniquely determine comp (\(times\)) and id.

This kind of type class would not have been possible in previous versions of GHC; with the new kind system, however, we can abstract over kinds!2 Now, let’s create types for the additive and multiplicative monoids over the natural numbers:

If you got this far…

I hope you enjoyed that! I can’t express enough my thanks to the people who came before me and helped me indirectly to refine my ideas and understanding of the relationship between monads and monoids. Additionally, a shout-out to the GHC team for adding kind polymorphism!