โ† back to foundations

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.

A B C f g g ∘ f
Scheme

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.

A idₐ
Scheme

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.

Scheme

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.

Scheme

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 jkthe natural framework, which models information processing as a six-stage pipeline where each stage composes into the next.

Scheme

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: xIdentity morphism
f :: A -> B(define (f a) ...)def f(a: A) -> BArrow from A to B
h.(g.f) = (h.g).fverified aboveverified aboveAssociativity law
f . id = fverified aboveverified aboveIdentity law
Neighbors

Other paper pages

Related foundations

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.

Ready for the real thing? Read Milewski's post. The challenges at the end are particularly good.