module Cat.Functor.Bifunctor where
Bifunctorsπ
private variable o h oβ hβ oβ hβ oβ hβ : Level C D E : Precategory oβ hβ module Cat[,] {o h oβ hβ} {C : Precategory o h} {D : Precategory oβ hβ} = Cat Cat[ C , D ]
A bifunctor
from
and
to
is a functor of two arguments. Traditionally, a bifunctor is defined as
having its domain be a product category, so that
would have type
In those terms, a bifunctor acts on both of its arguments
simultaneously, having a single action _β_ on morphisms.
For technical reasons, we instead prefer to define bifunctors with a functor category in their codomain, so that In these terms, we can evaluate at an object to get a functor and evaluating this at gives the action of on a pair of objects. The action of on a morphism behaves as a βwhiskeringβ operator, being a map which varies the second parameter, leaving fixed. The action of on a morphism is a natural transformation whose components, having type generate the complementary whiskering operation.
Bifunctor : Precategory o h β Precategory oβ hβ β Precategory oβ hβ β Type _ Bifunctor C D E = Functor C Cat[ D , E ]
{-# DISPLAY Functor {o} {β} {_} {_} C (Cat[_,_] {oβ} {ββ} {oβ} {ββ} D E) = Bifunctor {o} {β} {oβ} {ββ} {oβ} {ββ} C D E #-} module Bifunctor (F : Bifunctor C D E) where private module C = Precategory C module D = Precategory D module E = Cat E variable a b c d : β C β w x y z : β D β
More on the technical reasons.
The mechanism Mikan uses for reifying normal forms for display to the user favours definitions that can be written entirely in terms of the module system. If we defined bifunctors with a product argument, the joint action on morphisms would be disqualified from being written infix, since the module system provides no facility for currying a function.
private open module rβ X = Functor (F .Functor.Fβ X) public renaming (Fβ to infix 35 _βΆ_) using (Fβ) open module rβ {a b} (f : C.Hom a b) = _=>_ (F .Functor.Fβ f) public renaming (Ξ· to infix 35 _β_) using ()
Publicly opening private module aliases ensures that only the symbols
Fβ, _β_,
and _βΆ_
are in scope, but not the intermediate modules rβ and rβ, ensuring that a term like
f β A will not be recovered as f rβ.β A.
The rest of this module contains helpers for working with the two
functorial actions. First, we write two little helper functions that
allow eliding the βunchangingβ argument of the whiskerings. The names
lmap and rmap are named
left and right after the direction of
the triangles _β_
and _βΆ_.
lmap : C.Hom a b β E.Hom (Fβ a x) (Fβ b x) lmap f = f β _ rmap : D.Hom x y β E.Hom (Fβ a x) (Fβ a y) rmap f = _ βΆ f
These operations are both functorial by themselves. For rmap, we show this by
projecting from the functor
For lmap,
functoriality of
gives us a path of natural transformations, so we must project the
identity between the underlying maps as an additional step.
rmap-id : a βΆ D.id {x} β‘ E.id rmap-β : (f : D.Hom y z) (g : D.Hom x y) β a βΆ (f D.β g) β‘ (a βΆ f) E.β (a βΆ g) rmap-id = F .Functor.Fβ _ .Functor.F-id rmap-β = F .Functor.Fβ _ .Functor.F-β lmap-id : C.id {a} β x β‘ E.id lmap-β : (f : C.Hom b c) (g : C.Hom a b) β (f C.β g) β x β‘ (f β x) E.β (g β x) lmap-id = F .Functor.F-id Β·β _ lmap-β f g = F .Functor.F-β f g Β·β _
Finally, the naturality squares for each
pictured below, show that lmap and rmap commute past
eachother.
lrmap : β f g β (f β y) E.β (a βΆ g) β‘ (b βΆ g) E.β (f β x) lrmap f g = F .Functor.Fβ f .is-natural _ _ g rlmap : β g f β (b βΆ g) E.β (f β x) β‘ (f β y) E.β (a βΆ g) rlmap f g = sym (lrmap g f)
Horizontal compositionπ
A bifunctor provides two identical, but not definitionally equal,
ways of acting on both coordinates. For definiteness, we define the
horizontal composition operation to be the
left-hand-side of lrmap.
_β_ : β {a b x y} β C.Hom a b β D.Hom x y β E.Hom (F Β· a Β· x) (F Β· b Β· y) _β_ Ξ² Ξ± = (Ξ² β _) E.β (_ βΆ Ξ±)
A pair of short calculations shows that this operation is βfunctorial in both variablesβ.
β-id : β {a x} β C.id {a} β D.id {x} β‘ E.id β-id = C.id β D.id β‘β¨β© (C.id β _) E.β (_ βΆ D.id) β‘β¨ E.eliml lmap-id β©β‘ _ βΆ D.id β‘β¨ rmap-id β©β‘ E.id β β-β : β {a b c x y z} β {f : C.Hom b c} {g : C.Hom a b} {f' : D.Hom y z} {g' : D.Hom x y} β (f C.β g) β (f' D.β g') β‘ (f β f') E.β (g β g') β-β {f = f} {g} {f'} {g'} = (f C.β g) β (f' D.β g') β‘β¨β© (f C.β g β _) E.β (_ βΆ f' D.β g') β‘β¨ apβ E._β_ (lmap-β _ _) (rmap-β _ _) β©β‘ ((f β _) E.β (g β _)) E.β (_ βΆ f') E.β (_ βΆ g') β‘β¨ E.extendr (E.extendl (lrmap _ _)) β©β‘ ((f β _) E.β (_ βΆ f')) E.β (g β _) E.β (_ βΆ g') β‘β¨β© (f β f') E.β (g β g') β
As special cases of functoriality, we recover the whiskerings as a special case of horizontal composition.
lmap-β : β {a b x} (f : C.Hom a b) β f β x β‘ f β D.id lmap-β f = E.intror rmap-id rmap-β : β {x y a} (f : D.Hom x y) β a βΆ f β‘ C.id β f rmap-β f = E.introl lmap-id
Associated functorsπ
Evaluating
at an object
gives a functor
by definition. Since this functor acts by rmap, we call this the functor
associated to
on the Right.
Right : C.Ob β Functor D E Right A = F .Functor.Fβ A
In the other direction, we must write out the functor
associated to
on the Left, given an
object
in components.
Left : D.Ob β Functor C E Left X .Functor.Fβ A = Fβ A X Left X .Functor.Fβ f = f β X Left X .Functor.F-id = lmap-id Left X .Functor.F-β = lmap-β
module βΆ {A} = Fr (Right A) hiding (Fβ ; Fβ) module β {A} = Fr (Left A) hiding (Fβ ; Fβ)
By swapping the positions of lmap and rmap, we can turn a bifunctor
of
and
to
into a bifunctor of
and
to
module _ {C : Precategory o h} {D : Precategory oβ hβ} {E : Precategory oβ hβ} where private module C = Precategory C module D = Precategory D module E = Precategory E record Make-bifunctor : Type (o β oβ β oβ β h β hβ β hβ) where field Fβ : β C β β β D β β β E β lmap : β {a b x} β C.Hom a b β E.Hom (Fβ a x) (Fβ b x) rmap : β {x y a} β D.Hom x y β E.Hom (Fβ a x) (Fβ a y) lmap-id : β {a x} β lmap {a} {x = x} C.id β‘ E.id rmap-id : β {x a} β rmap {x} {a = a} D.id β‘ E.id lmap-β : β {a b c x} (f : C.Hom b c) (g : C.Hom a b) β lmap {x = x} (f C.β g) β‘ lmap f E.β lmap g rmap-β : β {x y z a} (f : D.Hom y z) (g : D.Hom x y) β rmap {a = a} (f D.β g) β‘ rmap f E.β rmap g lrmap : β {a b x y} (f : C.Hom a b) (g : D.Hom x y) β lmap f E.β rmap g β‘ rmap g E.β lmap f make-bifunctor : Make-bifunctor β Bifunctor C D E {-# INLINE make-bifunctor #-} make-bifunctor mm = record { Fβ = Ξ» x β record { Fβ = mm.Fβ x ; Fβ = mm.rmap ; F-id = mm.rmap-id ; F-β = mm.rmap-β } ; Fβ = Ξ» x β record { Ξ· = Ξ» _ β mm.lmap x ; is-natural = Ξ» x y z β mm.lrmap _ _ } ; F-id = ext Ξ» _ β mm.lmap-id ; F-β = Ξ» f g β ext Ξ» _ β mm.lmap-β _ _ } where module mm = Make-bifunctor mm module _ (F : Bifunctor C D E) where private open module F = Bifunctor F open Functor -- Defining Flip in components instead of using make-bifunctor avoids -- introducing a new "Flip.Right" which is distinct from Left. -- -- This is basically the only avoidable case of generativity.
Flip : Bifunctor D C E Flip .Fβ = Left Flip .Fβ f .Ξ· A = A βΆ f Flip .Fβ f .is-natural x y g = rlmap _ _ Flip .F-id = ext Ξ» _ β rmap-id Flip .F-β f g = ext Ξ» _ β rmap-β _ _
Finally, we can Uncurry
into a functor
using the horizontal composition defined above.
Uncurry : Functor (C ΓαΆ D) E Uncurry .Fβ = uncurry F.Fβ Uncurry .Fβ = uncurry _β_ Uncurry .F-id = β-id Uncurry .F-β _ _ = β-β
module _ {oβ hβ oβ hβ oβ hβ : _} {C : Precategory oβ hβ} {D : Precategory oβ hβ} {E : Precategory oβ hβ} {F G : Bifunctor C D E} where private module C = Precategory C module D = Precategory D module E = Cat E variable a b c d : β C β w x y z : β D β module F = Bifunctor F module G = Bifunctor G open _=>_ module Binatural (eta : F => G) where abstract natural-β : β {f : C.Hom a b} {x} β eta Β· _ Β· _ E.β (f F.β x) β‘ (f G.β x) E.β eta Β· _ Β· _ natural-β = eta .is-natural _ _ _ Ξ·β _ natural-βΆ : β {a} {f : D.Hom x y} β eta Β· _ Β· _ E.β (a F.βΆ f) β‘ (a G.βΆ f) E.β eta Β· _ Β· _ natural-βΆ = eta .Ξ· _ .is-natural _ _ _ natural-β : β {f : C.Hom a b} {g : D.Hom x y} β eta Β· _ Β· _ E.β (f F.β g) β‘ (f G.β g) E.β eta Β· _ Β· _ natural-β = E.pulll natural-β β E.extendr natural-βΆ private open module etaβ a = _=>_ (eta .Ξ· a) public right : β {x} β F.Right x => G.Right x right = eta .Ξ· _ left : β {x} β F.Left x => G.Left x left .Ξ· x = eta .Ξ· _ .Ξ· _ left .is-natural x y f = natural-β open Binatural using (natural-β ; natural-βΆ ; natural-β) public biisoβisoβΏ : (i : β x y β F Β· x Β· y E.β G Β· x Β· y) β (β {x y z} (f : C.Hom x y) β (f G.β z) E.β i x z .E.to β‘ i y z .E.to E.β (f F.β z)) β (β {x y z} (f : D.Hom x y) β (z G.βΆ f) E.β i z x .E.to β‘ i z y .E.to E.β (z F.βΆ f)) β F Cat[,].β G {-# INLINE biisoβisoβΏ #-} biisoβisoβΏ i n1 n2 = isoβisoβΏ (Ξ» x β isoβisoβΏ (i x) Ξ» {x y} f β n2 f) Ξ» {x y} f β ext (Ξ» z β n1 f) record Make-binatural : Type (oβ β oβ β hβ β hβ β hβ) where field Ξ· : (c : C.Ob) β (d : D.Ob) β E.Hom (F.Fβ c d) (G.Fβ c d) is-natural-β : β {c1 c2 : C.Ob} (f : C.Hom c1 c2) (d : D.Ob) β Ξ· c2 d E.β (f F.β d) β‘ (f G.β d) E.β Ξ· c1 d is-natural-βΆ : β (c : C.Ob) {d1 d2 : D.Ob} (f : D.Hom d1 d2) β Ξ· c d2 E.β (c F.βΆ f) β‘ (c G.βΆ f) E.β Ξ· c d1 make-binatural : Make-binatural β F => G {-# INLINE make-binatural #-} make-binatural mk = record { Ξ· = Ξ» x β record { Ξ· = Ξ» y β mk.Ξ· x y ; is-natural = Ξ» y z f β mk.is-natural-βΆ x f } ; is-natural = Ξ» x y f β ext Ξ» z β mk.is-natural-β f z } where module mk = Make-binatural mk