Jon Sterling, started 16th Dec 2022

\begin{code}

{-# OPTIONS --safe --without-K #-}

open import UF.FunExt

module Categories.NaturalTransformation (fe : Fun-Ext) where

open import Categories.Category fe
open import Categories.Functor fe
open import MLTT.Spartan
open import UF.Base
open import UF.Equiv
open import UF.Sets
open import UF.Sets-Properties
open import UF.Subsingletons
open import UF.Subsingletons-FunExt
open import UF.Subsingletons-Properties

module natural-transformation (๐“’ : precategory ๐“ค ๐“ฅ) (๐““ : precategory ๐“ค' ๐“ฅ') where
 private
  module ๐“’ = precategory ๐“’
  module ๐““ = precategory ๐““

 open functor-of-precategories ๐“’ ๐““

 module _ (F G : functor) where
  private
   module F = functor F
   module G = functor G

  transf : ๐“ค โŠ” ๐“ฅ' ฬ‡
  transf = (A : ๐“’.ob) โ†’ ๐““.hom (F.ob A) (G.ob A)

  transf-is-set : is-set transf
  transf-is-set  =
   ฮ -is-set fe ฮป _ โ†’
   ๐““.hom-is-set (F.ob _) (G.ob _)

  is-natural : transf โ†’ ๐“ค โŠ” ๐“ฅ โŠ” ๐“ฅ' ฬ‡
  is-natural ฮฑ =
   (A B : ๐“’.ob) (f : ๐“’.hom A B)
   โ†’ ๐““.seq (F.hom f) (ฮฑ B) ๏ผ ๐““.seq (ฮฑ A) (G.hom f)

  record nat-transf : ๐“ค โŠ” ๐“ฅ โŠ” ๐“ฅ' ฬ‡ where
   constructor make
   field
    str : transf
    ax : is-natural str

  module nat-transf-as-sum where
   to-sum : nat-transf โ†’ ฮฃ is-natural
   to-sum ฮฑ = let open nat-transf ฮฑ in str , ax

   from-sum : ฮฃ is-natural โ†’ nat-transf
   from-sum ฮฑ = make (prโ‚ ฮฑ) (prโ‚‚ ฮฑ)

   to-sum-is-equiv : is-equiv to-sum
   prโ‚ (prโ‚ to-sum-is-equiv) = from-sum
   prโ‚‚ (prโ‚ to-sum-is-equiv) _ = refl
   prโ‚ (prโ‚‚ to-sum-is-equiv) = from-sum
   prโ‚‚ (prโ‚‚ to-sum-is-equiv) (make str ax) = refl

   equiv : nat-transf โ‰ƒ ฮฃ is-natural
   equiv = to-sum , to-sum-is-equiv

  being-natural-is-prop : {ฮฑ : transf} โ†’ is-prop (is-natural ฮฑ)
  being-natural-is-prop =
   ฮ -is-prop fe ฮป _ โ†’
   ฮ -is-prop fe ฮป _ โ†’
   ฮ -is-prop fe ฮป _ โ†’
   ๐““.hom-is-set _ _

  nat-transf-is-set : is-set nat-transf
  nat-transf-is-set =
   equiv-to-set
    nat-transf-as-sum.equiv
    (ฮฃ-is-set transf-is-set ฮป _ โ†’
     props-are-sets being-natural-is-prop)

  module _ {ฮฑ ฮฒ : nat-transf} where
   to-nat-transf-๏ผ : nat-transf.str ฮฑ ๏ผ nat-transf.str ฮฒ โ†’ ฮฑ ๏ผ ฮฒ
   to-nat-transf-๏ผ h =
    equivs-are-lc
     nat-transf-as-sum.to-sum
     nat-transf-as-sum.to-sum-is-equiv
     (to-ฮฃ-๏ผ (h , being-natural-is-prop _ _))

  -- TODO : characterize identity type

 module _ (F : functor) where
  private module F = functor F
  transf-idn : transf F F
  transf-idn A = ๐““.idn (F.ob A)

  abstract
   transf-idn-natural : is-natural F F transf-idn
   transf-idn-natural A B f =
    ๐““.seq (F.hom f) (๐““.idn _) ๏ผโŸจ ๐““.idn-R _ _ _ โŸฉ
    F.hom f ๏ผโŸจ ๐““.idn-L _ _ _ โปยน โŸฉ
    ๐““.seq (๐““.idn _) (F.hom f) โˆŽ

  nat-transf-idn : nat-transf F F
  nat-transf-idn = make transf-idn transf-idn-natural

 module _ (F G H : functor) where
  private
   module F = functor F
   module G = functor G
   module H = functor H

  module _ (ฮฑ : transf F G) (ฮฒ : transf G H) where
   transf-seq : transf F H
   transf-seq A = ๐““.seq (ฮฑ A) (ฮฒ A)

   module _ (ฮฑ-nat : is-natural F G ฮฑ) (ฮฒ-nat : is-natural G H ฮฒ) where
    abstract
     transf-seq-natural : is-natural F H transf-seq
     transf-seq-natural A B f =
      ๐““.seq (F.hom f) (๐““.seq (ฮฑ B) (ฮฒ B))
       ๏ผโŸจ ๐““.assoc _ _ _ _ _ _ _ โŸฉ
      ๐““.seq (๐““.seq (F.hom f) (ฮฑ B)) (ฮฒ B)
       ๏ผโŸจ ap (ฮป x โ†’ ๐““.seq x (ฮฒ B)) (ฮฑ-nat _ _ _) โŸฉ
      ๐““.seq (๐““.seq (ฮฑ A) (G.hom f)) (ฮฒ B)
       ๏ผโŸจ ๐““.assoc _ _ _ _ _ _ _ โปยน โŸฉ
      ๐““.seq (ฮฑ A) (๐““.seq (G.hom f) (ฮฒ B))
       ๏ผโŸจ ap (๐““.seq (ฮฑ A)) (ฮฒ-nat _ _ _) โŸฉ
      ๐““.seq (ฮฑ A) (๐““.seq (ฮฒ A) (H.hom f))
       ๏ผโŸจ ๐““.assoc _ _ _ _ _ _ _ โŸฉ
      ๐““.seq (๐““.seq (ฮฑ A) (ฮฒ A)) (H.hom f) โˆŽ

  nat-transf-seq : nat-transf F G  โ†’ nat-transf G H โ†’ nat-transf F H
  nat-transf-seq ฮฑ ฮฒ =
   let module ฮฑ = nat-transf ฮฑ in
   let module ฮฒ = nat-transf ฮฒ in
   make
    (transf-seq ฮฑ.str ฮฒ.str)
    (transf-seq-natural ฮฑ.str ฮฒ.str ฮฑ.ax ฮฒ.ax)

 module _ (F G : functor) (ฮฑ : transf F G) where
  transf-idn-L : transf-seq F F G (transf-idn F) ฮฑ ๏ผ ฮฑ
  transf-idn-L =
   dfunext fe ฮป _ โ†’
   ๐““.idn-L _ _ _

  transf-idn-R : transf-seq F G G ฮฑ (transf-idn G) ๏ผ ฮฑ
  transf-idn-R =
   dfunext fe ฮป _ โ†’
   ๐““.idn-R _ _ _

 module _
  (F G H I : functor)
  (ฮฑ : transf F G)
  (ฮฒ : transf G H)
  (ฮณ : transf H I)
  where
  transf-assoc
   : transf-seq F G I ฮฑ (transf-seq G H I ฮฒ ฮณ)
   ๏ผ transf-seq F H I (transf-seq F G H ฮฑ ฮฒ) ฮณ
  transf-assoc =
   dfunext fe ฮป _ โ†’
   ๐““.assoc _ _ _ _ _ _ _

 module nat-transf-laws (F G : functor) (ฮฑ : nat-transf F G) where
  module ฮฑ = nat-transf ฮฑ

  nat-transf-idn-L : nat-transf-seq _ _ _ (nat-transf-idn F) ฮฑ ๏ผ ฮฑ
  nat-transf-idn-L =
   to-nat-transf-๏ผ F G
    (transf-idn-L F G ฮฑ.str)

  nat-transf-idn-R : nat-transf-seq _ _ _ ฮฑ (nat-transf-idn G) ๏ผ ฮฑ
  nat-transf-idn-R =
   to-nat-transf-๏ผ F G
    (transf-idn-R F G ฮฑ.str)

 module _ (F G H I : functor) (ฮฑ : nat-transf F G) (ฮฒ : nat-transf G H) (ฮณ : nat-transf H I) where
  nat-transf-assoc
   : nat-transf-seq _ _ _ ฮฑ (nat-transf-seq _ _ _ ฮฒ ฮณ)
   ๏ผ nat-transf-seq _ _ _ (nat-transf-seq _ _ _ ฮฑ ฮฒ) ฮณ
  nat-transf-assoc =
   to-nat-transf-๏ผ F I
    (transf-assoc F G H I _ _ _)

 module functor-category where
  structure : category-structure (๐“ค โŠ” ๐“ฅ โŠ” ๐“ค' โŠ” ๐“ฅ') (๐“ค โŠ” ๐“ฅ โŠ” ๐“ฅ')
  structure = functor , nat-transf , nat-transf-idn , nat-transf-seq

  axioms : precategory-axioms structure
  axioms =
   let open nat-transf-laws in
   nat-transf-is-set ,
   nat-transf-idn-L ,
   nat-transf-idn-R ,
   nat-transf-assoc

  precat : precategory (๐“ค โŠ” ๐“ฅ โŠ” ๐“ค' โŠ” ๐“ฅ') (๐“ค โŠ” ๐“ฅ โŠ” ๐“ฅ')
  precat = make structure axioms

module _ {๐“’ : precategory ๐“ฃ ๐“ค} {๐““ : precategory ๐“ฃ' ๐“ค'} {๐“” : precategory ๐“ฅ ๐“ฆ} where
 private
  module ๐“’ = precategory ๐“’
  module ๐““ = precategory ๐““
  module ๐“” = precategory ๐“”

 open functor-of-precategories
 open natural-transformation

 module horizontal-composition
  {F1 G1 : functor ๐“’ ๐““}
  {F2 G2 : functor ๐““ ๐“”}
  (ฮฑ : nat-transf ๐“’ ๐““ F1 G1)
  (ฮฒ : nat-transf ๐““ ๐“” F2 G2)
  where

  private
   F3 = composite-functor.fun F1 F2
   G3 = composite-functor.fun G1 G2
   module F1 = functor F1
   module F2 = functor F2
   module G1 = functor G1
   module G2 = functor G2
   module F3 = functor F3
   module G3 = functor G3
   module ฮฑ = nat-transf ฮฑ
   module ฮฒ = nat-transf ฮฒ

  hcomp-str : transf ๐“’ ๐“” F3 G3
  hcomp-str A = ๐“”.seq (ฮฒ.str (F1.ob A)) (G2.hom (ฮฑ.str A))

  abstract
   hcomp-ax : is-natural ๐“’ ๐“” F3 G3 hcomp-str
   hcomp-ax A B f =
    ๐“”.seq (F2.hom (F1.hom f)) (๐“”.seq (ฮฒ.str (F1.ob B)) (G2.hom (ฮฑ.str B)))
     ๏ผโŸจ ๐“”.assoc _ _ _ _ _ _ _ โŸฉ
    ๐“”.seq (๐“”.seq (F3.hom f) (ฮฒ.str (F1.ob B))) (G2.hom (ฮฑ.str B))
     ๏ผโŸจ ap (ฮป x โ†’ ๐“”.seq x _) h0 โŸฉ
    ๐“”.seq (๐“”.seq (ฮฒ.str (F1.ob A)) (G2.hom (F1.hom f))) (G2.hom (ฮฑ.str B))
     ๏ผโŸจ ๐“”.assoc _ _ _ _ _ _ _ โปยน โŸฉ
    ๐“”.seq (ฮฒ.str (F1.ob A)) (๐“”.seq (G2.hom (F1.hom f)) (G2.hom (ฮฑ.str B)))
     ๏ผโŸจ ap (๐“”.seq (ฮฒ.str (F1.ob A))) h1 โŸฉ
    ๐“”.seq (ฮฒ.str (F1.ob A)) (๐“”.seq (G2.hom (ฮฑ.str A)) (G3.hom f))
     ๏ผโŸจ ๐“”.assoc _ _ _ _ _ _ _ โŸฉ
    ๐“”.seq (๐“”.seq (ฮฒ.str (F1.ob A)) (G2.hom (ฮฑ.str A))) (G3.hom f) โˆŽ
    where
     h0
      : ๐“”.seq (F2.hom (F1.hom f)) (ฮฒ.str (F1.ob B))
      ๏ผ ๐“”.seq (ฮฒ.str (F1.ob A)) (G2.hom (F1.hom f))
     h0 = ฮฒ.ax (F1.ob A) (F1.ob B) (F1.hom f)

     h1
      : ๐“”.seq (G2.hom (F1.hom f)) (G2.hom (ฮฑ.str B))
      ๏ผ ๐“”.seq (G2.hom (ฮฑ.str A)) (G3.hom f)
     h1 =
      ๐“”.seq (G2.hom (F1.hom f)) (G2.hom (ฮฑ.str B))
       ๏ผโŸจ G2.preserves-seq _ _ _ _ _ โปยน โŸฉ
      G2.hom (๐““.seq (F1.hom f) (ฮฑ.str B))
       ๏ผโŸจ ap G2.hom (ฮฑ.ax _ _ _) โŸฉ
      G2.hom (๐““.seq (ฮฑ.str A) (G1.hom f))
       ๏ผโŸจ G2.preserves-seq _ _ _ _ _ โŸฉ
      ๐“”.seq (G2.hom (ฮฑ.str A)) (G3.hom f) โˆŽ

  hcomp : nat-transf ๐“’ ๐“” F3 G3
  hcomp = make hcomp-str hcomp-ax

 module left-whiskering
  {G H : functor ๐““ ๐“”}
  (W : functor ๐“’ ๐““)
  (ฮฒ : nat-transf ๐““ ๐“” G H)
  where

  private
   W-G = composite-functor.fun W G
   W-H = composite-functor.fun W H
   module W = functor W
   module H = functor H
   module ฮฒ = nat-transf ฮฒ

  whisk-str : transf _ _ W-G W-H
  whisk-str A = ฮฒ.str (W.ob A)

  whisk-ax : is-natural _ _ W-G W-H whisk-str
  whisk-ax A B f = ฮฒ.ax (W.ob A) (W.ob B) (W.hom f)

  whisk : nat-transf _ _ W-G W-H
  whisk = make whisk-str whisk-ax

 module right-whiskering
  {G H : functor ๐“’ ๐““}
  (ฮฒ : nat-transf _ _ G H)
  (W : functor ๐““ ๐“”)
  where

  private
   G-W = composite-functor.fun G W
   H-W = composite-functor.fun H W
   module W = functor W
   module G = functor G
   module H = functor H
   module ฮฒ = nat-transf ฮฒ

  whisk-str : transf _ _ G-W H-W
  whisk-str A = W.hom (ฮฒ.str A)

  whisk-ax : is-natural _ _ G-W H-W whisk-str
  whisk-ax A B f =
   ๐“”.seq (W.hom (G.hom f)) (W.hom (ฮฒ.str B)) ๏ผโŸจ W.preserves-seq _ _ _ _ _ โปยน โŸฉ
   W.hom (๐““.seq (G.hom f) (ฮฒ.str B)) ๏ผโŸจ ap W.hom (ฮฒ.ax _ _ _) โŸฉ
   W.hom (๐““.seq (ฮฒ.str A) (H.hom f)) ๏ผโŸจ W.preserves-seq _ _ _ _ _ โŸฉ
   ๐“”.seq (W.hom (ฮฒ.str A)) (W.hom (H.hom f)) โˆŽ

  whisk : nat-transf ๐“’ ๐“” G-W H-W
  whisk = make whisk-str whisk-ax


module
 _
  {๐“’ : precategory ๐“ฃ ๐“ค} {๐““ : precategory ๐“ฅ ๐“ฆ}
  (open functor-of-precategories)
  (F : functor ๐“’ ๐““)
 where
 open natural-transformation

 private
  module ๐““ = precategory ๐““
  module F = functor F
  1[๐“’] = identity-functor.fun ๐“’
  1[๐““] = identity-functor.fun ๐““
  1[๐“’]-F = composite-functor.fun 1[๐“’] F
  F-1[๐““] = composite-functor.fun F 1[๐““]
  [๐“’,๐““] = functor-category.precat ๐“’ ๐““
  module [๐“’,๐““] = precategory [๐“’,๐““]

 left-unitor : [๐“’,๐““].hom 1[๐“’]-F F
 nat-transf.str left-unitor A = ๐““.idn (F.ob A)
 nat-transf.ax left-unitor A B f =
  ๐““.seq (F.hom f) (๐““.idn (F.ob B)) ๏ผโŸจ ๐““.idn-R _ _ _ โŸฉ
  F.hom f ๏ผโŸจ ๐““.idn-L _ _ _ โปยน โŸฉ
  ๐““.seq (๐““.idn (F.ob A)) (F.hom f) โˆŽ

 left-unitor-inverse : [๐“’,๐““].hom F 1[๐“’]-F
 nat-transf.str left-unitor-inverse A = ๐““.idn (F.ob A)
 nat-transf.ax left-unitor-inverse A B f =
  ๐““.seq (F.hom f) (๐““.idn (F.ob B)) ๏ผโŸจ ๐““.idn-R _ _ _ โŸฉ
  F.hom f ๏ผโŸจ ๐““.idn-L _ _ _ โปยน โŸฉ
  ๐““.seq (๐““.idn (F.ob A)) (F.hom f) โˆŽ

 right-unitor : [๐“’,๐““].hom F-1[๐““] F
 nat-transf.str right-unitor A = ๐““.idn (F.ob A)
 nat-transf.ax right-unitor A B f =
  ๐““.seq (F.hom f) (๐““.idn (F.ob B)) ๏ผโŸจ ๐““.idn-R _ _ _ โŸฉ
  F.hom f ๏ผโŸจ ๐““.idn-L _ _ _ โปยน โŸฉ
  ๐““.seq (๐““.idn (F.ob A)) (F.hom f) โˆŽ

 right-unitor-inverse : [๐“’,๐““].hom F F-1[๐““]
 nat-transf.str right-unitor-inverse A = ๐““.idn (F.ob A)
 nat-transf.ax right-unitor-inverse A B f =
  ๐““.seq (F.hom f) (๐““.idn (F.ob B)) ๏ผโŸจ ๐““.idn-R _ _ _ โŸฉ
  F.hom f ๏ผโŸจ ๐““.idn-L _ _ _ โปยน โŸฉ
  ๐““.seq (๐““.idn (F.ob A)) (F.hom f) โˆŽ

 abstract
  left-unitor-is-section : [๐“’,๐““].seq left-unitor left-unitor-inverse ๏ผ [๐“’,๐““].idn 1[๐“’]-F
  left-unitor-is-section =
   to-nat-transf-๏ผ _ _ _ _
    (dfunext fe ฮป A โ†’
     ๐““.seq (๐““.idn (F.ob A)) (๐““.idn (F.ob A)) ๏ผโŸจ ๐““.idn-L _ _ _ โŸฉ
     ๐““.idn (F.ob A) โˆŽ)

  left-unitor-is-retraction : [๐“’,๐““].seq left-unitor-inverse left-unitor ๏ผ [๐“’,๐““].idn F
  left-unitor-is-retraction =
   to-nat-transf-๏ผ _ _ _ _
    (dfunext fe ฮป A โ†’
     ๐““.seq (๐““.idn (F.ob A)) (๐““.idn (F.ob A)) ๏ผโŸจ ๐““.idn-L _ _ _ โŸฉ
     ๐““.idn (F.ob A) โˆŽ)

  right-unitor-is-section : [๐“’,๐““].seq right-unitor right-unitor-inverse ๏ผ [๐“’,๐““].idn F-1[๐““]
  right-unitor-is-section =
   to-nat-transf-๏ผ _ _ _ _
    (dfunext fe ฮป A โ†’
     ๐““.seq (๐““.idn (F.ob A)) (๐““.idn (F.ob A)) ๏ผโŸจ ๐““.idn-L _ _ _ โŸฉ
     ๐““.idn (F.ob A) โˆŽ)

  right-unitor-is-retraction : [๐“’,๐““].seq right-unitor-inverse right-unitor ๏ผ [๐“’,๐““].idn F
  right-unitor-is-retraction =
   to-nat-transf-๏ผ _ _ _ _
    (dfunext fe ฮป A โ†’
     ๐““.seq (๐““.idn (F.ob A)) (๐““.idn (F.ob A)) ๏ผโŸจ ๐““.idn-L _ _ _ โŸฉ
     ๐““.idn (F.ob A) โˆŽ)


module
 _
  {๐“’ : precategory ๐“ฃ ๐“ค} {๐““ : precategory ๐“ฅ ๐“ฆ}
  {๐“” : precategory ๐“ฃ' ๐“ค'} {๐“• : precategory ๐“ฅ' ๐“ฆ'}
  (open functor-of-precategories)
  (F : functor ๐“’ ๐““) (G : functor ๐““ ๐“”) (H : functor ๐“” ๐“•)
 where
 open natural-transformation

 private
  [๐“’,๐“•] = functor-category.precat ๐“’ ๐“•
  module [๐“’,๐“•] = precategory [๐“’,๐“•]
  module ๐“• = precategory ๐“•
  module H = functor H
  module G = functor G
  module F = functor F
  F-G = composite-functor.fun F G
  G-H = composite-functor.fun G H
  F-[G-H] = composite-functor.fun F G-H
  [F-G]-H = composite-functor.fun F-G H

 associator : [๐“’,๐“•].hom F-[G-H] [F-G]-H
 nat-transf.str associator A = ๐“•.idn (H.ob (G.ob (F.ob A)))
 nat-transf.ax associator A B f =
  ๐“•.seq (H.hom (G.hom (F.hom f))) (๐“•.idn _) ๏ผโŸจ ๐“•.idn-R _ _ _ โŸฉ
  H.hom (G.hom (F.hom f)) ๏ผโŸจ ๐“•.idn-L _ _ _ โปยน โŸฉ
  ๐“•.seq (๐“•.idn _) (H.hom (G.hom (F.hom f))) โˆŽ

 associator-inverse : [๐“’,๐“•].hom [F-G]-H F-[G-H]
 nat-transf.str associator-inverse A = ๐“•.idn (H.ob (G.ob (F.ob A)))
 nat-transf.ax associator-inverse A B f =
  ๐“•.seq (H.hom (G.hom (F.hom f))) (๐“•.idn _) ๏ผโŸจ ๐“•.idn-R _ _ _ โŸฉ
  H.hom (G.hom (F.hom f)) ๏ผโŸจ ๐“•.idn-L _ _ _ โปยน โŸฉ
  ๐“•.seq (๐“•.idn _) (H.hom (G.hom (F.hom f))) โˆŽ

 abstract
  associator-is-section : [๐“’,๐“•].seq associator associator-inverse ๏ผ [๐“’,๐“•].idn F-[G-H]
  associator-is-section =
   to-nat-transf-๏ผ _ _ _ _
    (dfunext fe ฮป A โ†’
     ๐“•.seq (๐“•.idn _) (๐“•.idn _) ๏ผโŸจ ๐“•.idn-L _ _ _ โŸฉ
     ๐“•.idn _ โˆŽ)

  associator-is-retraction : [๐“’,๐“•].seq associator-inverse associator ๏ผ [๐“’,๐“•].idn [F-G]-H
  associator-is-retraction =
   to-nat-transf-๏ผ _ _ _ _
    (dfunext fe ฮป A โ†’
     ๐“•.seq (๐“•.idn _) (๐“•.idn _) ๏ผโŸจ ๐“•.idn-L _ _ _ โŸฉ
     ๐“•.idn _ โˆŽ)

\end{code}