โ† back to index

The Yoneda Lemma

Bartosz Milewski ยท 2015 ยท Category Theory for Programmers, Ch. 14

Prereqs: ๐Ÿž Ch10 (Natural Transformations), ๐Ÿž Ch13 (Representable Functors). 10 min.

Nat(Hom(a, –), F) ≅ F(a). Natural transformations from a hom-functor to any functor F are in bijection with elements of F(a). An object is completely determined by its relationships to every other object. The entire natural transformation crystallizes from a single value: what you assign to the identity morphism.

The hom-functor: an object seen from the outside

Fix an object a in a category. The hom-functor Hom(a, –) maps every object x to the set of morphisms from a to x. In Haskell, this is the Reader a functor: Reader a x = a -> x. It captures everything a can "see" in the category.

Scheme

The Yoneda bijection: one value determines everything

A natural transformation α from Hom(a, –) to a functor F is a polymorphic function: for every x, it takes an (a → x) and returns an F x. The wpYoneda lemma says: every such natural transformation is determined by a single element of F(a). To recover it, apply α to the identity morphism. To go the other way, take an element of F(a) and fmap any function over it.

natural transformations Nat(Hom(a,–), F) elements F(a) α ↦ α(idₐ) x ↦ (f ↦ F(f)(x)) one seed at idₐ determines the whole flower
a f₁ f₂ f₃ b₁ b₂ b₃ F F(f₁) F(f₂) F(f₃) F(a) knowing F(a) determines F on every morphism out of a
Scheme

Why it works: naturality forces your hand

The naturality condition for α says: α_y ˆ Hom(a, f) = F(f) ˆ α_x. Apply this at x = a with the identity morphism id_a. The left side gives α_y(f). The right side gives F(f)(α_a(id_a)). So once you choose α_a(id_a), the value at every other component is forced: α_y(f) = fmap f (α_a(id_a)). One seed, the whole flower.

Scheme

Length: a natural transformation in the wild

length is a natural transformation from List to Const Int. It turns a [a] into an Int regardless of the element type. Yoneda says this nat. trans. corresponds to a single element of Const Int (a) when the domain is a hom-functor, but here we see the pattern: length is natural because it doesn't inspect the elements. It commutes with fmap: length (map f xs) = length xs.

Scheme

Yoneda embedding: objects as their morphisms

Set F = Hom(b, –) in the Yoneda lemma. Then Nat(Hom(a, –), Hom(b, –)) ≅ Hom(b, a). Every natural transformation between hom-functors comes from a morphism. This means the mapping a ↦ Hom(a, –) is a full and faithful embedding: it preserves all morphisms, losing nothing. An object is completely characterized by its web of relationships.

Scheme

Co-Yoneda: the contravariant dual

The co-Yoneda lemma applies the same trick to the contravariant hom-functor Hom(–, a). Now: Nat(Hom(–, a), F) ≅ F(a) for contravariant functors F. In Haskell: (forall x . (x → a) → F x) ≅ F a. The identity trick works the same way, but the arrows point backwards.

Scheme

Continuation-passing style: Yoneda with F = Identity

Set F = Identity in the Yoneda lemma. Then (forall x . (a → x) → x) ≅ a. A value of type a is the same thing as a function that takes any continuation a → x and produces an x. This is continuation-passing style. The Yoneda lemma is the theoretical justification for CPS transforms.

Scheme

Notation reference

Blog / Haskell Scheme Meaning
C(a, x) / Reader a x(lambda (x) ...)Hom-set / hom-functor
Nat(C(a,-), F) ≅ F a(phi alpha) = (alpha id)Yoneda lemma
phi alpha = alpha id(alpha (lambda (x) x))Extract element at identity
psi x h = fmap h x(map h fa)Rebuild nat. trans. from element
forall x . (a -> x) -> x(to-cps val)CPS / Yoneda at Identity
a ↦ C(a, -)(alpha h) = (h . g)Yoneda embedding
Neighbors

Milewski chapters

Paper pages that use these concepts

Foundations (Wikipedia)

Translation notes

The blog post proves Yoneda for locally small categories with Set-valued functors. This page stays in the programming world: functors are type constructors with fmap, natural transformations are polymorphic functions, and the bijection is demonstrated by round-tripping through phi and psi. The key subtlety the blog post emphasizes: in Haskell, polymorphic functions are automatically natural (parametric polymorphism guarantees naturality via the "theorem for free"). In category theory, naturality is an additional condition that must be checked. The co-Yoneda section on this page uses predicates for concreteness. Milewski states it more generally for presheaves (contravariant functors to Set). The Yoneda embedding (full and faithful) is the deeper payoff: it means every category embeds into a functor category, so abstract morphisms can always be studied as concrete functions between sets.

Ready for the real thing? Read the blog post. Start with the naturality condition, then trace how alpha at id_a determines everything.