Mon 26 May 2008
Kan Extensions III: As Ends and Coends
Posted by Edward Kmett under Haskell , Category Theory , Mathematics , Kan ExtensionsGrant B. asked me to post the derivation for the right and left Kan extension formula used in previous Kan Extension posts (1,2). For that we can turn to the definition of Kan extensions in terms of ends, but first we need to take a couple of steps back to find a way to represent (co)ends in Haskell.
Dinatural Transformations
Rather than repeat the definition here, we'll just note we can define a dinatural transformation in Haskell letting polymorphism represent the family of morphisms.
type Dinatural f g = forall a. f a a -> g a a
Ends
So what is an end?
An end is a universal dinatural transformation from some object e to some functor s.
Diving into the formal definition:
Given a functor
, and end of
is a pair
where
is an object of
and omega is a dinatural transformation from e to S such that given any other dinatural transformation
to S from another object x in
, there exists a unique morphism
, such that
for every
in
.
We usually choose to write ends as
, and abuse terminology calling
the end of
.
Note this uses a dinatural transformation from an object
in
, which we can choose to represent an arbitrary dinatural transformation from an object
to a functor
in terms of a dinatural transformation from the constant bifunctor:
newtype Const x a b = Const { runConst :: x }
This leaves us with the definition of a dinatural transformation from an object as:
Dinatural (Const x) s ~ forall a. Const x a a -> s a a
but the universal quantification over the Const term is rather useless and the Const bifunctor is supplying no information so we can just specialize that down to:
type DinaturalFromObject x s = x -> forall a. s a a
Now, clearly for any such transformation, we could rewrite it trivially using the definition:
type End s = forall a. s a a
with
= id.
type DinaturalFromObject x s = x -> End s
And so End above fully complies with the definition for an end, and we just say e = End s abusing the terminology as earlier. The function
= id is implicit.
For End to be a proper end, we assume that s is contravariant in its first argument and covariant in its second argument.
Example: Hom
A good example of this is (->) in Haskell, which is as category theory types would call it the Hom functor for Hask. Then End (->) = forall a. a -> a which has just one inhabitant id if you discard cheating inhabitants involving fix or undefined.
We write
or
to denote a -> b. Similarly we use
to denote an exponential object within a category, and since in Haskell we have first class functions using the same syntax this also translates to a -> b. We'll need these later.
Example: Natural Transformations
If we define:
newtype HomFG f g a b = HomFG { runHomFG :: f a -> g b }
then we could of course choose to define natural transformations in terms of End as:
type Nat f g = End (HomFG f g) -- forall a. f a -> g a
Right Kan Extension as an End
Turning to Wikipedia or Categories for the Working Mathematician you can find the following definition for right Kan extension of
along
in terms of ends.

Working by rote, we note that
is just c -> K m as noted above, and that
is then just (c -> K m) -> T m'. So now we just have to take the end over that, and read off:
newtype RanT f g c m m' = (c -> K m) -> T m' type Ran f g c = End (RanT f g c)
Which, modulo newtype noise is the same as the type previously supplied type:
newtype Ran f g c = Ran { runRan :: forall m. (c -> f m) -> g m }
Coends
The derivation for the left Kan extension follows similarly from defining coends over Hask in terms of existential quantification and copowers as products.
The coend derivation is complicated slightly by Haskell's semantics. Disposing of the constant bifunctor as before we get:
type DinaturalToObject s c = forall a. (s a a -> c)
Which since we want to be able to box up the s a a term separately, we need to use existential quantification.
(forall a. s a a -> c) ~ (exists a. s a a) -> c
We cannot represent this directly in terms of a type annotation in Haskell, but we can do so with a data type:
data Coend f = forall a. Coend (f a a)
Recall that in Haskell, existential quantification is represented by using universal quantification outside of the type constructor.
The main difference is that in our coend
the function
is now runCoend instead of id, because we have a Coend data constructor around the existential. Technicalities make it a little more complicated than that even, because you can't define a well-typed runCoend and have to use pattern matching on the Coend data constructor to avoid having an existentially quantified type escape the current scope, but the idea is the same.
Left Kan Extension as a Coend
Then given the definition for the left Kan extension of
along
as a coend:

we can read off:
data LanT k t c m m' = LanT (k m -> c) (t m) type Lan k t c = Coend (LanT k t c)
Which is almost isomorphic to the previously supplied type:
data Lan k t c = forall m. Lan (k m -> c) (t m)
except for the fact that we had to use two layers of data declarations when using the separate Coend data type, so we introduced an extra place for a
to hide.
A newtype isn't allowed to use existential quantification in Haskell, so this form forces a spurious case analysis that we'd prefer to do without. This motivates why category-extras uses a separate definition for Lan rather than the more elaborate definition in terms of Coend.

May 27th, 2008 at 5:27 am
Awesome! This helped my understanding a lot, thanks.
May 27th, 2008 at 3:45 pm
Hey! Stop writing posts faster than I can read them :-)
June 13th, 2008 at 5:41 pm
I’m curious as to how you would motivate Kan extensions as a functional programming (specifically Haskell) tool. The right Kan extension almost looks like the CPS-transform of “a”, except with variant answer types. Shan’s thesis on page 73 shows how the ordinary function type is modified via the presence of delimited control into something like a right Kan extension.
Care to shed some light on this (and its dual)?
June 14th, 2008 at 8:16 am
Well, you can look at the Codensity monad (Ran m m) of a monad m as a weaker form of ContT which isn’t allowed to use the type of the ultimate return value to ‘pull any tricks’. Consequently, it has fewer inhabitants than ContT and we can say more about them. My favorite example so far is in:
http://wwwtcs.inf.tu-dresden.de/~voigt/mpc08.pdf
That paper shows that you can substitute a reference to the Codensity monad of a free monad in many places where you’d use the original free monad and change the asymptotic performance of the resulting algorithm because the continuation ‘linearizes’ the cost of repeatedly traversing the tree by piling the cost up in the continuation so you only traverse once.
I’d conjecture that the left Kan extension of a cofree comonad should let us pile up the cost of mapping or extending cofree comonads similarly. Though here I have yet to actually generate any performance results here due to other distractions.
We _can_ however definitely do the same thing with the Yoneda and Coyoneda lemmas. The resulting (co)monads fit very closely to the operational characteristics of the base monad.
If you transform a monad with Yoneda you get a monad that has the same overall costs for return and >>=, but where if your monad had a non constant cost for map (such as in the free monad of anything other than the trivial functor that maps everything to _|_), you can fuse the costs of all of your maps together by accumulating them and applying them all at once during the next (>>=) (or when you extract f from the Yoneda f).
Similarly CoYoneda accumulates maps for comonads, which is a win if map is expensive (such as in the cofree comonad case).
You can get a feel for why this works by looking at:
http://comonad.com/haskell/category-extras/src/Control/Functor/Yoneda.hs
and noting what fmap does and how >>= works for Yoneda. and how fmap and extend work for CoYoneda.
In category-extras I have generalized all of the constructive algorithmic morphisms that use the free monad or cofree comonad (histomorphisms, futumorphisms, dynamorphisms, chronomorphisms) to support the codensity monad of the free monad, density comonad of the cofree comonad, Yoneda of the free monad, Coyoneda of the cofree comonad, etc by defining a couple of extra typeclasses that express their ‘Free-Like’ behavior in the spirit of the above linked paper by Voigtlaender in
http://comonad.com/haskell/category-extras/src/Control/Monad/Free.hs
The generalization can be seen in use here:
http://comonad.com/haskell/category-extras/src/Control/Morphism/Futu.hs
and the instances for Codensity here:
http://comonad.com/haskell/category-extras/src/Control/Monad/Codensity.hs