Natural Transformations
Bartosz Milewski ยท 2015 ยท Category Theory for Programmers, Ch. 10
Prereqs: ๐ Ch7 (Functors). 8 min.
A natural transformation maps between functors while preserving structure. The naturality condition says: it doesn't matter whether you transform first and then map, or map first and then transform. In Haskell, parametric polymorphism gives you naturality for free: any function F a -> G a that works uniformly for all types satisfies the naturality square.
Natural transformations: morphisms between functors
A functor maps objects and morphisms from one category to another. A natural transformation maps between two such functors. For functors F and G from category C to D, a natural transformation alpha provides, for each object a in C, a morphism alpha_a : F(a) -> G(a) in D. These morphisms are called components. In
the natural framework, each processing stage is a functor, and the transitions between stages are natural transformations.
The naturality condition
The components of a natural transformation can't be arbitrary. They must satisfy the naturality condition: for every morphism f : a -> b, the following square commutes.
In words: transform then map = map then transform. This is what makes the transformation "natural" rather than ad hoc.
Length: List -> Const Int
length is a natural transformation from the List functor to Const Int. The Const functor ignores its type parameter: Const Int a is just Int regardless of a. Length doesn't care what's in the list, only how many elements there are. That type-blindness is naturality at work.
Theorems for free: polymorphism = naturality
In Haskell, any polymorphic function alpha :: F a -> G a (where F and G are functors) automatically satisfies the naturality condition. You don't need to check it. Parametric polymorphism means the function can't inspect the type, so it can't break the naturality square. This is Wadler's "theorems for free": the type signature alone generates the equation fmap g . alpha = alpha . fmap g as a free theorem.
Functor categories
Natural transformations compose. If alpha : F -> G and beta : G -> H, then (beta . alpha)_a = beta_a . alpha_a gives a natural transformation from F to H. This makes functors between two categories C and D into a category of their own: [C, D], the functor category. Objects are functors, morphisms are natural transformations, and the identity natural transformation maps each F(a) to itself.
Naturality as a design constraint
The naturality condition is surprisingly restrictive. How many natural transformations are there from Maybe to List? Only two: either send Nothing -> [] and Just x -> [x], or send everything to []. The type signature Maybe a -> [a] pins down the implementation almost completely.
Notation reference
| Blog / Haskell | Scheme | Meaning |
|---|---|---|
| alpha :: F a -> G a | (alpha x) | Component of a natural transformation at type a |
| safeHead :: [a] -> Maybe a | (safe-head lst) | Natural transformation List -> Maybe |
| length :: [a] -> Const Int a | (my-length lst) | Natural transformation List -> Const Int |
| fmap g . alpha = alpha . fmap g | (equal? (map g (alpha xs)) (alpha (map g xs))) | Naturality condition |
| (beta . alpha)_a = beta_a . alpha_a | (beta (alpha x)) | Vertical composition |
| [C, D] | โ | Functor category: functors as objects, natural transformations as morphisms |
Neighbors
Milewski chapters
- ๐ Chapter 8 โ Functoriality: bifunctors, contravariance, profunctors
- (prev) ๐ Chapter 7 โ Functors: structure-preserving maps
Paper pages that use natural transformations
- ๐ Fritz 2020 โ Markov categories use natural transformations for copy and discard
- ๐ Spivak 2013 โ database queries as natural transformations between functors
- ๐ Leinster 2021 โ entropy as a natural transformation
Foundations (Wikipedia)
Translation notes
The blog post covers three layers: natural transformations, functor categories, and the 2-category Cat. This page covers the first two. The 2-category perspective (categories as objects, functors as 1-morphisms, natural transformations as 2-morphisms) and horizontal composition are omitted. The blog post also discusses contravariant natural transformations with Op functors and shows that naturality for polymorphic functions between Haskell functors is automatic via parametricity (Wadler's "Theorems for Free"). Scheme has no type system to enforce this, so naturality is verified by testing rather than proof. The core insight still holds: a transformation that works uniformly without inspecting elements will satisfy the naturality square.