*Previously: Existentials.*

# Double Yoneda

If you squint hard enough, the Yoneda lemma:

could be interpreted as the representable functor acting as the unit with respect to taking the end. It takes an and returns an . Let’s keep this in mind.

We are going to need an identity that involves higher-order natural transformations between two higher-order functors. These are actually the functors that we’ve encountered before. They are parameterized by objects in , and their action on functors (co-presheaves) is to apply those functors to objects. They are the “give me a functor and I’ll apply it to my favorite object” kind of functors.

We need a natural transformation between two such functors, and we can express it as an end:

Here’s the trick: replace these functors with their Yoneda equivalents:

Notice that this is now a mapping between two hom-sets in the functor category, the first one being:

We can now use the corollary of the Yoneda lemma to replace the set of natural transformation between these two hom-functors with the hom-set:

But this is again a natural transformation between two hom-functors, so it can be further reduced to . The result is:

We’ve used the Yoneda lemma twice, so this trick is called the double-Yoneda.

# Profunctors

It turns out that the prism also has a functor-polymorphic representation, but it uses profunctors in place of regular functors. A profunctor is a functor of two arguments, but its action on arrows has a twist. Here’s the Haskell definition:

class Profunctor p where dimap :: (a' -> a) -> (b -> b') -> (p a b -> p a' b')

It lifts a pair of functions, where the first one goes in the opposite direction.

In category theory, the “twist” is encoded by using the opposite category , so a profunctor is defined a functor from to .

The prime example of a profunctor is the hom-functor which, on objects, assigns the set to every pair .

Before we talk about the profunctor representation of prisms and lenses, there is a simple optic called `Iso`

. It’s defined by a pair of functions:

from :: s -> a to :: b -> t

The key observation here is that such a pair of arrows is an element of the hom set in the category between the pair and the pair :

The “twist” of using reverses the direction of the first arrow.

`Iso`

has a simple profunctor representation:

type Iso s t a b = forall p. Profunctor p => p a b -> p s t

This formula can be translated to category theory as an end in the profunctor category:

Profunctor category is a category of co-presheaves . We can immediately apply the double Yoneda identity to it to get:

which shows the equivalence of the two representations.

# Tambara Modules

Here’s the profunctor representation of a prism:

type Prism s t a b = forall p. Choice p => p a b -> p s t

It looks almost the same as `Iso`

, except that the quantification goes over a smaller class of profunctors called `Choice`

(or cocartesian). This class is defined as:

class Profunctor p => Choice where left' :: p a b -> p (Either a c) (Either b c) right' :: p a b -> p (Either c a) (Either c b)

Lenses can also be defined in a similar way, using the class of profunctors called `Strong`

(or cartesian).

class Profunctor p => Strong where first' :: p a b -> p (a, c) (b, c) second' :: p a b -> p (c, a) (c, b)

Profunctor categories with these structures are called Tambara modules. Tambara formulated them in the context of monoidal categories, for a more general tensor product. Sum (`Either`

) and product `(,)`

are just two special cases.

A Tambara module is an object in a profunctor category with additional structure defined by a family of morphisms:

with some naturality and coherence conditions.

Lenses and prisms can thus be defined as ends in the appropriate Tambara modules

We can now use the double Yoneda trick to get the usual representation.

The problem is, we don’t know in what category the result should be. We know the objects are pairs , but what are the morphisms between them? It turns out this problem was solved in a paper by Pastro and Street. The category in question is the Kleisli category for a particular promonad. This category is now better known as . Let me explain.

# Double Yoneda with Adjunctions

The double Yoneda trick worked for an unconstrained category of functors. We need to generalize it to a category with some additional structure (for instance, a Tambara module).

Let’s say we start with a functor category and endow it with some structure, resulting in another functor category . It means that there is a (higher-order) forgetful functor that forgets this additional structure. We’ll also assume that there is the right adjoint functor that freely generates the structure.

We will re-start the derivation of double Yoneda using the forgetful functor

Here, and are objects in and is a functor in .

We perform the Yoneda trick the same way as before to get:

Again, we have two sets of natural transformations, the first one being:

The adjunction tells us that

The right-hand side is a hom-set in the functor category . Plugging this back into the original formula, we get

This is the set of natural transformations between two hom-functors, so we can use the corollary of the Yoneda lemma to replace it with:

We can then use the adjunction again, in the opposite direction, to get:

or, using the end notation:

Finally, we use the Yoneda lemma again to get:

This is the action of the higher-order functor on the hom-functor , the result of which is applied to .

The composition of two functors that form an adjunction is a monad . This is a monad in the functor category . Altogether, we get:

# Profunctor Representation of Lenses and Prisms

The previous formula can be immediately applied to the category of Tambara modules. The forgetful functor takes a Tambara module and maps it to a regular profunctor , an object in the functor category . We replace and with pairs of objects. We get:

The only missing piece is the higher order monad —a monad operating on profunctors.

The key observation by Pastro and Street was that Tambara modules are higher-order coalgebras. The mappings:

can be thought of as components of a natural transformation

By continuity of hom-sets, we can move the end over to the right:

We can use this to define a higher order functor that acts on profunctors:

so that the family of Tambara mappings can be written as a set of natural transformations :

Natural transformations are morphisms in the category of profunctors, and such a morphism is, by definition, a coalgebra for the functor .

Pastro and Street go on showing that is more than a functor, it’s a comonad, and the Tambara structure is not just a coalgebra, it’s a comonad coalgebra.

What’s more, there is a monad that is adjoint to this comonad:

When a monad is adjoint to a comonad, the comonad coalgebras are isomorphic to monad algebras—in this case, Tambara modules. Indeed, the algebras are given by natural transformations:

Substituting the formula for ,

by continuity of the hom-set (with the coend in the negative position turning into an end),

using the currying adjunction,

and the Yoneda lemma, we get

which is the Tambara structure .

is exactly the monad that appears on the right-hand side of the double-Yoneda with adjunctions. This is because every monad can be decomposed into a pair of adjoint functors. The decomposition we’re interested in is the one that involves the Kleisli category of free algebras for . And now we know that these algebras are Tambara modules.

All that remains is to evaluate the action of on the represesentable functor:

It’s a matter of simple substitution:

and using the Yoneda lemma to replace with . The result is:

This is exactly the existential represenation of the lens and the prism:

This was an encouraging result, and I was able to derive a few other optics using the same approach.

The idea was that Tambara modules were just one example of a monoidal action, and it could be easily generalized to other types of optics, like `Grate`

, where the action is replaced by the (contravariant in ) action (or `c->a`

, in Haskell).

There was just one optic that resisted that treatment, the `Traversal`

. The breakthrough came when I was joined by a group of talented students at the Applied Category Theory School in Oxford.

*Next: Traversals.*

July 1, 2021 at 2:36 am

Up to this point I’ve had few problems following your developments. However after repeated attempts Yoneda and lenses elude me. I get the gist. However

it feels like the apex of a large ediface and I’m left wodering “so what”.In

particular in earlier installments you were careful to shadow the mathematics

with fragments of Haskell and C++ this seems to have tailed off here.

Two points:

– how does Yoneda enable traversals and through this lenses; and/or

– if Yoneda was not available what would break, and why.

Hopefully a constructive input on what I’ve found to be a wonderful exposition

and demonsrtation of the rich interplay between theory and practice. All of

the Category theory texts I’ve worked through seem to struggle around this

point too.

July 1, 2021 at 10:00 am

Think of this 3-part series as a taste of what research-level category theory feels like. I tried to make it as approachable as possible, but the background required to understand it is quite extensive. And without the Yoneda lemma there would be no category theory.

July 6, 2021 at 7:41 am

Hello Bartosz, I was looking for way to contact you but presumably, comments below your posts are the best way. So I hope this question will be best related to this particular post. So my question is about “related” Profunctors constructed as follows:

p12 is a Profunctor going from (C1_op x C2_op) x (C1 x C2) -> Set12

p1 is a Profunctor going from (C1_op x C1) -> Set1

p2 is a Profunctor going from C2_op x C2 -> Set2

Is there a way to construct Set12 directly from sets Set1 and Set2, or in other words, p12 from p1 and p2?

Many thanks.

July 6, 2021 at 10:45 am

You can just take the cartesian product of two sets. You automatically get a profunctor.