Category: The Essence of Composition
Bartosz Milewski ยท 2014 ยท Category Theory for Programmers, Ch. 1
A category is objects plus arrows (morphisms) plus composition. Composition is associative. Every object has an identity arrow. That's it. Every compositional structure you'll meet is a special case of this.
Composition โ the one operation
If there's an arrow from A to B and an arrow from B to C, there must be an arrow from A to C: their composition. In Haskell it's the dot operator. In math it's the circle. Here we build it from scratch.
Identity โ the arrow that does nothing
Every object A has an identity morphism idA that goes from A back to A. Composing any arrow with identity gives back the same arrow. It's the "do nothing" function, and it's required to exist for every object in the category.
Associativity โ parentheses don't matter
Given three arrows f, g, h that chain together, h . (g . f) = (h . g) . f. You can group any way you like. This is what makes long pipelines unambiguous: there's only one way to compose a chain.
The category of types and functions
Milewski's running example: objects are types (Integer, Boolean, String), arrows are functions between types. Composition is function composition. Identity is the identity function. This category is called Set (or Hask for Haskellers). Every program you write lives here.
Why composition matters
Milewski's argument: human cognition has a limited chunk budget. We manage complexity by decomposing problems into pieces and recomposing solutions. Composition is not an optional design pattern. It's how finite minds handle infinite complexity. Surface area grows slower than volume: good interfaces expose less than they hide. This chunking constraint is the starting point for
the natural framework, which models information processing as a six-stage pipeline where each stage composes into the next.
Notation reference
| Haskell | Scheme | Python | Meaning |
|---|---|---|---|
| g . f | (compose g f) | compose(g, f) | Composition: apply f then g |
| id | (lambda (x) x) | lambda x: x | Identity morphism |
| f :: A -> B | (define (f a) ...) | def f(a: A) -> B | Arrow from A to B |
| h.(g.f) = (h.g).f | verified above | verified above | Associativity law |
| f . id = f | verified above | verified above | Identity law |
Neighbors
Other paper pages
- ๐ Spivak 2013 โ categories applied to databases and data migration
- ๐ Leinster 2021 โ entropy as a functor, another category-theoretic lens
- ๐ Staton 2025 โ composition of programs follows the same axioms
Related foundations
- ๐ Judson Ch.4 Isomorphisms โ group isomorphisms as the algebraic instance of categorical isomorphisms
Foundations (Wikipedia)
Translation notes
All examples use total functions over simple types. The original post uses Haskell, where the category Hask includes bottom (non-termination) and laziness, which our Scheme and Python translations ignore. Milewski also discusses the category of sets and partial functions, and poses six challenges (e.g., implementing memoize, checking composition of partial functions). Those are omitted here but worth doing. The surface-area-vs-volume argument is informal motivation, not a theorem.