โ† back to index

The Yoneda Embedding

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

Prereqs: ๐Ÿž Ch14 (The Yoneda Lemma). 8 min.

Every category embeds fully and faithfully into its presheaf category [Cop, Set]. The Yoneda embedding Y sends each object a to its hom-functor C(a, –), and no information is lost: distinct morphisms stay distinct, and every natural transformation between hom-functors comes from a morphism in C.

C a b c Y [C, Set] C(a, –) C(b, –) C(c, –) fully faithful: no information lost

The embedding: objects become hom-functors

Fix a category C. For each object a, the mapping C(a, –) is a covariant functor from C to Set. The Yoneda embedding Y maps each object a to this functor, and each morphism f : a → b to a natural transformation between the corresponding hom-functors. We build a small category (a 3-object graph) and embed it.

Scheme

Fully faithful: no information lost

A functor is faithful if it's injective on hom-sets (distinct morphisms stay distinct), and full if it's surjective (every natural transformation between the image functors comes from a morphism). The Yoneda embedding is both. The proof is the Yoneda lemma itself: natural transformations [C, Set](C(a, –), C(b, –)) are in bijection with C(b, a). Reversing the direction gives the contravariant version.

Scheme

Contravariant Yoneda: the co-Yoneda embedding

The dual version uses the contravariant hom-functor C(–, a). This embeds C into the presheaf category [Cop, Set]. The isomorphism becomes: Nat(C(–, a), C(–, b)) ≅ C(a, b). No reversal this time: the directions match.

Scheme

Preserving and reflecting isomorphisms

A fully faithful functor both preserves and reflects isomorphisms. If a ≅ b in C, then Y(a) ≅ Y(b) in [C, Set] (preservation). Conversely, if Y(a) ≅ Y(b), then a ≅ b (reflection). Two objects are isomorphic if and only if they "look the same from the outside" — they have the same pattern of incoming and outgoing morphisms.

Scheme

In Haskell: CPS is the Yoneda embedding

In Haskell, the Yoneda embedding becomes: forall x. (a -> x) -> (b -> x) ≅ b -> a. This is continuation-passing style. Given a function btoa :: b -> a, you construct a polymorphic function that takes any continuation a -> x and pre-composes it. Recovering btoa is just applying the identity continuation.

Scheme

Preorder example: the embedding as implication

In a preorder (where hom-sets are either empty or singletons), the Yoneda embedding becomes: a ≤ b if and only if for all x, x ≤ a implies x ≤ b. Knowing all the morphisms into an object completely determines its position in the order. This is the "behavioral" characterization: an object is defined by what maps into it.

Scheme

Notation reference

Blog / Haskell Scheme Meaning
C(a, –)(yoneda-embed a)Covariant hom-functor (representable)
C(–, a)(co-yoneda-embed a)Contravariant hom-functor (presheaf)
Y : C → [C, Set](yoneda-embed a)Yoneda embedding functor
[C, Set](C(a,–), C(b,–)) ≅ C(b,a)(nat-trans-count a b)Fully faithful: nat-trans = morphisms
forall x. (a→x)→(b→x) ≅ b→a(from-y k)CPS encoding (Haskell version)
a ≤ b ⇔ ∀x. x≤a ⇒ x≤b(every ... '(1 2 3))Preorder embedding as implication
Neighbors

Milewski chapters

  • ๐Ÿž Chapter 14 โ€” The Yoneda Lemma: the isomorphism this embedding relies on
  • (next) Chapter 16 โ€” Limits and Colimits

Paper pages that use these concepts

Foundations (Wikipedia)

Translation notes

The blog post works in Haskell, where the Yoneda embedding manifests as the polymorphic isomorphism forall x. (a -> x) -> (b -> x) ≅ b -> a. This page uses explicit finite categories in Scheme, which makes the hom-sets visible as concrete lists. The trade-off: you can see the bijection by counting, but you lose the polymorphic elegance. The blog post also discusses naturality of the Yoneda isomorphism in both arguments (forming a higher-order natural transformation between evaluation and hom-pairing). That machinery requires 2-categorical language and is omitted here. The preorder example is faithful to the original. The CPS example is the same construction Milewski gives, translated from Haskell tabulate/index style to explicit lambda application.

Ready for the real thing? Read the blog post. Start with the fully faithful proof, then trace the CPS encoding.