โ† back to index

Ends and Coends

Bartosz Milewski ยท 2017 ยท Category Theory for Programmers, Ch. 20

Prereqs: ๐Ÿž Ch11 (Limits and Colimits), ๐Ÿž Ch10 (Natural Transformations). 12 min.

An end is a product indexed by objects, constrained to be dinatural: it must work for both covariant and contravariant arguments simultaneously. The end of a profunctor P is c P(c,c). Natural transformations fall out as a special case: Nat(F,G) = ∫a Hom(F(a), G(a)). Reverse the arrows and you get coends: generalized sums that quotient by dinaturality.

Profunctors โ€” functors of mixed variance

A profunctor P : Cop × C → Set takes two arguments: one contravariant (first slot), one covariant (second slot). The canonical example is the hom-functor Hom(a,b). The operation dimap lifts morphisms into both slots: contravariant on the left, covariant on the right.

Scheme
w P(a,a) P(b,b) P(a,b) αₐ αᵇ P(id,f) P(f,id)

The wedge condition

A wedge for a profunctor P is an object w with a family of morphisms αa : w → P(a,a) (one per object a) that satisfy the wedge condition: for any morphism f : a → b, the two paths from w to P(a,b) must agree. One path lifts f covariantly, the other contravariantly. This is the dinaturality constraint. It is weaker than naturality because it only touches diagonal elements P(a,a).

Scheme

Ends โ€” the universal wedge

The end of a profunctor P, written c P(c,c), is the universal wedge: the terminal object in the category of wedges. It comes with projections πa : ∫c P(c,c) → P(a,a) satisfying the wedge condition, and every other wedge factors through it uniquely. In Haskell, the end is forall a. p a a -- a polymorphic value that works at every type simultaneously.

Scheme

Natural transformations as an end

The set of natural transformations between functors F and G is an end: Nat(F,G) = ∫a Hom(F(a), G(a)). The profunctor here is P(a,b) = Hom(F(a), G(b)). Taking diagonal elements gives Hom(F(a), G(a)), which is the set of components at a. The wedge condition enforces the naturality square: G(f) . αa = αb . F(f).

Scheme

Coends โ€” dinatural sums

The coend of a profunctor P, written c P(c,c), dualizes the end. Instead of a universal wedge (product + constraint), it is a universal cowedge (coproduct + quotient). Take the disjoint union of all P(a,a), then identify elements that are connected by dinaturality: for every f : a → b, we glue P(id,f)(x) to P(f,id)(x). In Haskell, this is the existential type exists a. p a a.

Scheme

The ninja Yoneda lemma

The Yoneda lemma has an end/coend formulation. The end version: z Set(C(a,z), F(z)) ≅ F(a). A natural transformation from the representable Hom(a,–) to F is determined by where it sends the identity, giving an element of F(a). The coend version (ninja Yoneda): z C(z,a) × F(z) ≅ F(a). Every element paired with a morphism into a collapses to an element of F(a) by applying the morphism.

Scheme

Notation reference

Blog / Haskell Scheme Meaning
dimap f g h(dimap-hom f g h)Lift into both slots of a profunctor
c P(c,c)(filter wedge-ok? product)End: universal wedge
c P(c,c)(union-find elements ids)Coend: coproduct modulo dinaturality
forall a. p a a(end as filtered product)End in Haskell
exists a. p a a(coend as quotient of coproduct)Coend in Haskell
Nat(F,G) = ∫a Hom(F a, G a)(nat-transformations)Natural transformations as an end
z C(z,a) × F(z) ≅ F(a)(evaluate pair)Ninja Yoneda lemma
Neighbors

Milewski chapters

Foundations (Wikipedia)

Translation notes

The blog post develops ends and coends via Haskell's type system, where the end is forall a. p a a and the coend is an existential. This page computes both over finite categories instead: the end is a filtered product (keep only elements satisfying the wedge condition), and the coend is a union-find quotient of a coproduct. The ninja Yoneda lemma is verified by showing that pairs (morphism, element) collapse to exactly F(a) after the dinaturality quotient. Milewski also covers profunctor composition via coends and the continuity of the hom-functor (turning coends in the first argument into ends), which require infinite categories to be interesting and are omitted here.

Ready for the real thing? Read the blog post. Start with the wedge condition, then see how Nat(F,G) = ∫a Hom(F a, G a) makes naturality automatic.