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:
Int, String, List[User].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.
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.
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.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.
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
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.
∘ 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.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.
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.
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.