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 automatically 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. Natural transformations are how
the natural framework connects its six processing stages: each stage is a functor, and the transitions between them are natural transformations that preserve the information structure.
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
Another natural transformation: length. It maps from the List functor to the Const Int functor. The Const functor ignores its type parameter, so 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
Here is the deepest insight. 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 works by one formula for all types. It 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 naturality square forbids any other choice. 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 (where categories are objects, functors are 1-morphisms, and natural transformations are 2-morphisms) and horizontal composition are omitted for clarity. 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"). In Scheme, we have no type system to enforce this, so naturality is verified by testing rather than by proof. The key insight carries over regardless: a transformation that works uniformly without inspecting elements will satisfy the naturality square.