Category Theory for programmers
🔣 Chapter 1 of a series · The Essence of Composition

Category theory,
one arrow at a time

A category is just things and arrows between them that you can chain together. If you've ever written f andThen g, you already know the punchline. Let's make it precise — and draggable.

Category theory has a reputation for being abstract math for people who like abstract math. But for programmers there's a friendlier door in: a category is the mathematics of composition — the single idea your whole job is built on.

Strip everything else away and a category is two things:

  • Objects — dots. In programming, think types: Int, String, List[User].
  • Morphisms (arrows) — connections between objects. Think functions: Int => String.

That's the cast. The plot is one rule: if you have an arrow into a thing and an arrow out of it, you can join them into a single arrow that skips the middle. Composition. Everything else in this chapter is making that precise and discovering it obeys exactly two laws.

1Composition: the only move

Given f : A → B and g : B → C, a category guarantees a composite arrow g ∘ f : A → C. Read it right-to-left: "g after f." Feed in an A, get out a C, and the B in the middle disappears from the type.

Interactive · drag the objects Drag A, B, C around · then build the composite
g ∘ f : A → C
💡
This is exactly Scala's andThen / compose and Haskell's .. The whole discipline of building big programs out of small, well-typed pieces is living inside a category — you've been doing category theory without the vocabulary.

2The two laws (that's all of them)

For this to deserve the name "category," composition has to behave. There are precisely two rules — and both are things you already assume without thinking.

Identity — every object has a "do nothing" arrow

Each object A comes with an identity morphism idA : A → A. It's the unit of composition: gluing it on either end of any arrow changes nothing.

f ∘ idA  =  f  =  idB ∘ f

Interactive · identity arrows
f : A → B

Associativity — grouping doesn't matter

With three composable arrows f, g, h, it makes no difference how you parenthesize the chain. Both groupings produce the same arrow A → D, so we just write h ∘ g ∘ f.

Interactive · associativity
same arrow A → D either way
⚠️
Order gotcha for Scala devs: the math symbol reads right-to-left, like Haskell's .. So g ∘ f = g . f (Haskell) = f andThen g (Scala). Scala's compose matches the math order; andThen flips it.

3The same idea, in code

Types are objects, functions are morphisms, and the language gives you composition + identity out of the box. Flip between the two languages — notice the only real difference is the direction of the composition operator.

4Make the square commute

A diagram commutes when every path between two objects composes to the same arrow — it doesn't matter which way you walk. Here the top route is g ∘ f and the bottom route is k ∘ h, both from A to D. With f = x+1 and h = x·2 fixed, pick g and k so the two paths always agree.

Puzzle · choose g and k Tested against several inputs

5Check yourself

3 questions · instant feedback 0 / 3

6A taste of what's next

Here's the hook for Chapter 2. The two laws you just met — identity and composition — are so important that the most useful structures in functional programming are exactly the maps that preserve them. Such a structure-preserving map between categories is a functor.

You know one: map. A List is a functor. Mapping a function over it doesn't just transform elements — it respects composition (xs.map(f).map(g) == xs.map(g ∘ f)) and identity (xs.map(id) == xs). Watch it lift a plain function into the List context.

Preview · the List functor
List[Int] [
1
2
3
4
]
map lifts A ⇒ B into List[A] ⇒ List[B]
📦
Up next · Chapter 2
Types & Functions
Pinning down WHAT the objects and arrows actually are. The loneliest type, the most boring type, and why pure functions are the only ones that compose cleanly.