Monoid as a Category — When “Combining” Becomes “Moving”
Most developers first learn a monoid like this:
A set + a binary operation + an identity element
Examples:
It feels like a rule about values being added together.But category theory asks us to look at it differently. Instead of thinking about elements being combined,we think about actions being applied.
Stop Thinking About Values — Think About Transformations
Take a very simple monoid: natural numbers with addition.
Normally we say:
5 + 7 = 12
But we can reinterpret this in a completely different way.
Instead of treating 5 as a number, treat it as an action:
“Add 5 to something.”
That means 5 is no longer just a value — it becomes a function:
add5(x) = x + 5
add7(x) = x + 7Now something interesting happens.
Combining Numbers Becomes Composing Functions
If we apply add5 and then add7:
add7(add5(x)) = x + 12That is the same as:
add12(x)So:
Combining numbers is the same as composing transformations.
5 + 7 ≡ add7 ∘ add5We didn’t lose information. We just changed perspective.
The Identity Appears Naturally
What is the identity element of addition? 0
What does add0 do?
add0(x) = xIt does nothing. That is exactly the identity function.
So:
And composition is always associative:
(add2 ∘ add3) ∘ add4 = add2 ∘ (add3 ∘ add4)
Not because addition is associative — but because function composition is associative.
The Big Realization
We no longer need numbers.
We only need:
This structure is a category with one object.
And that is precisely what a monoid is.
A monoid is a single-object category.
The name even hints at it:
mono = one
Visual Intuition
Instead of imagining a set of values:
0,1,2,3,4,5...
Imagine a blob — a single space — and arrows that transform it:
(add 1)
(add 2)
(add 3)
(add 4)
...All arrows start and end at the same place. They differ only by how they move things inside. That’s a monoid as a category.
Kotlin Example — Seeing the Same Idea
Let’s model this idea directly.
Instead of storing numbers, store operations:
typealias Endo<A> = (A) -> A
Now define adders:
fun add(n: Int): Endo<Int> = { x -> x + n }Compose them:
val add5 = add(5)
val add7 = add(7)
val add12 = { x: Int -> add7(add5(x)) }
println(add12(10)) // 22We never added numbers. We composed actions. That’s the categorical monoid.
Recovering the Usual Monoid
Here’s the beautiful part. If you take all these arrows:
add0, add1, add2, add3...They form a set.
And composition between them behaves exactly like addition:
add5 ∘ add7 → add12So from the category we get the original monoid back.
This means:
The algebraic monoid and the categorical monoid are the same idea seen from two angles.
One sees values. The other sees transformations.
Why This Matters
This perspective explains why monoids appear everywhere in programming:
function pipelines
Nothing is being “added”. We are composing behaviors that share a common space.
