module Cat.Internal.Base {o β„“} (C : Precategory o β„“) where

Internal categoriesπŸ”—

We often think of categories as places where we can do mathematics. This is done by translating definitions into the internal language of some suitably structured class of categories, and then working within that internal language to prove theorems.

This is all fine and good, but there is an obvious question: what happens if we internalize the definition of a category? Such categories are (unsurprisingly) called internal categories, and are quite well-studied. The traditional definition goes as follows: Suppose is a category with pullbacks, fix a pair of objects be a pair of objects, and parallel maps

The idea is that and are meant to be the β€œobject of objects” and β€œobject of morphisms”, respectively, while the maps and assign each morphism to its domain and codomain. A diagram is a category internal to if it has an identity-assigning morphism a composition morphism where the pullback β€” given by the square below β€” is the object of composable pairs.

These must also satisfy left/right identity and associativity. The associativity condition is given by the square below, and we trust that the reader will understand why will not attempt to draw the identity constraints.

Encoding this diagram in a proof assistant is a nightmare. Even constructing the maps into we must speak about is a pile of painful proof obligations, and these scale atrociously when talking about iterated pullbacks.1

To solve the problem, we look to a simpler case: internal monoids in These are straightforward to define in diagrammatic language, but can also be defined in terms of representability! The core idea is that we can define internal structure in the category of presheaves on rather than directly in letting us us use the structure of the meta-language to our advantage. To ensure that the structure defined in presheaves can be internalized to we restrict ourselves to working with representable presheaves β€” which is equivalent to by the Yoneda lemma.

From a type theoretic point of view, this is akin to defining structure relative to an arbitrary context rather than in the smallest context possible. This relativisation introduces a new proof obligation: stability under substitution. We have to ensure that we have defined the same structure in every context, which translates to a naturality condition.

Representing internal morphismsπŸ”—

Let be a category, and be a diagram as before. We will define internal morphisms between generalised objects to be morphisms making the following diagram commute.

record Internal-hom
  {Cβ‚€ C₁ Ξ“ : Ob}
  (src tgt : Hom C₁ Cβ‚€) (x y : Hom Ξ“ Cβ‚€)
  : Type β„“ where
  no-eta-equality
  field
    ihom : Hom Ξ“ C₁
    has-src : src ∘ ihom ≑ x
    has-tgt : tgt ∘ ihom ≑ y

open Internal-hom

This definition may seem somewhat odd, but we again stress that we are working in the internal language of where it reads as the following typing rule:

Internal-hom-pathp
  : βˆ€ {Cβ‚€ C₁ Ξ“} {src tgt : Hom C₁ Cβ‚€} {x x' y y' : Hom Ξ“ Cβ‚€}
  β†’ {f : Internal-hom src tgt x y} {g : Internal-hom src tgt x' y'}
  β†’ (p : x ≑ x') (q : y ≑ y')
  β†’ f .ihom ≑ g .ihom
  β†’ PathP (Ξ» i β†’ Internal-hom src tgt (p i) (q i)) f g
Internal-hom-pathp p q r i .ihom = r i
Internal-hom-pathp {src = src} {f = f} {g = g} p q r i .has-src =
  is-propβ†’pathp (Ξ» i β†’ Hom-set _ _ (src ∘ r i) (p i)) (f .has-src) (g .has-src) i
Internal-hom-pathp {tgt = tgt} {f = f} {g = g} p q r i .has-tgt =
  is-propβ†’pathp (Ξ» i β†’ Hom-set _ _ (tgt ∘ r i) (q i)) (f .has-tgt) (g .has-tgt) i

Internal-hom-path
  : βˆ€ {Cβ‚€ C₁ Ξ“} {src tgt : Hom C₁ Cβ‚€} {x y : Hom Ξ“ Cβ‚€}
  β†’ {f g : Internal-hom src tgt x y}
  β†’ f .ihom ≑ g .ihom
  β†’ f ≑ g
Internal-hom-path p = Internal-hom-pathp refl refl p

private unquoteDecl eqv = declare-record-iso eqv (quote Internal-hom)

Internal-hom-set
  : βˆ€ {Ξ“ Cβ‚€ C₁} {src tgt : Hom C₁ Cβ‚€} {x y : Hom Ξ“ Cβ‚€}
  β†’ is-set (Internal-hom src tgt x y)
Internal-hom-set = Iso→is-hlevel! 2 eqv

instance
  H-Level-Internal-hom
    : βˆ€ {Ξ“ Cβ‚€ C₁} {src tgt : Hom C₁ Cβ‚€} {x y : Hom Ξ“ Cβ‚€} {n}
    β†’ H-Level (Internal-hom src tgt x y) (2 + n)
  H-Level-Internal-hom = basic-instance 2 Internal-hom-set

_ihomβ‚š
  : βˆ€ {Cβ‚€ C₁ Ξ“} {src tgt : Hom C₁ Cβ‚€} {x y : Hom Ξ“ Cβ‚€}
  β†’ {f g : Internal-hom src tgt x y}
  β†’ f ≑ g
  β†’ f .ihom ≑ g .ihom
_ihomβ‚š = ap ihom

infix -1 _ihomβ‚š

adjusti
    : βˆ€ {Ξ“ Cβ‚€ C₁} {src tgt : Hom C₁ Cβ‚€} {x x' y y' : Hom Ξ“ Cβ‚€}
  β†’ x ≑ x' β†’ y ≑ y' β†’ Internal-hom src tgt x y β†’ Internal-hom src tgt x' y'
adjusti p q f .ihom = f .ihom
adjusti p q f .has-src = f .has-src βˆ™ p
adjusti p q f .has-tgt = f .has-tgt βˆ™ q

We also must define the action of substitutions on internal morphisms. Zooming out to look at substitutions are morphisms and act on internal morphisms by precomposition.

_[_] : βˆ€ {Cβ‚€ C₁ Ξ“ Ξ”} {src tgt : Hom C₁ Cβ‚€} {x y : Hom Ξ” Cβ‚€}
     β†’ Internal-hom src tgt x y
     β†’ (Οƒ : Hom Ξ“ Ξ”)
     β†’ Internal-hom src tgt (x ∘ Οƒ) (y ∘ Οƒ)
(f [ Οƒ ]) .ihom = f .ihom ∘ Οƒ
(f [ Οƒ ]) .has-src = pulll (f .has-src)
(f [ Οƒ ]) .has-tgt = pulll (f .has-tgt)

infix 50 _[_]

That out of the way, we can define internal categories in terms of their internal morphisms.

record Internal-cat-on {Cβ‚€ C₁} (src tgt : Hom C₁ Cβ‚€) : Type (o βŠ” β„“) where
  no-eta-equality
  field
    idi : βˆ€ {Ξ“} β†’ (x : Hom Ξ“ Cβ‚€) β†’ Internal-hom src tgt x x
    _∘i_ : βˆ€ {Ξ“} {x y z : Hom Ξ“ Cβ‚€}
         β†’ Internal-hom src tgt y z β†’ Internal-hom src tgt x y
         β†’ Internal-hom src tgt x z

  infixr 40 _∘i_

Having rewritten the pullbacks from before β€” where the previous attempt at a definition ended β€” in terms of dependency in the meta-language, we can state the laws of an internal category completely analogously to their external counterparts!

  field
    idli : βˆ€ {Ξ“} {x y : Hom Ξ“ Cβ‚€} (f : Internal-hom src tgt x y)
         β†’ idi y ∘i f ≑ f
    idri : βˆ€ {Ξ“} {x y : Hom Ξ“ Cβ‚€} (f : Internal-hom src tgt x y)
         β†’ f ∘i idi x ≑ f
    associ : βˆ€ {Ξ“} {w x y z : Hom Ξ“ Cβ‚€}
           β†’ (f : Internal-hom src tgt y z)
           β†’ (g : Internal-hom src tgt x y)
           β†’ (h : Internal-hom src tgt w x)
           β†’ f ∘i g ∘i h ≑ (f ∘i g) ∘i h

However, we do need to add the stability conditions, ensuring that we have the same internal category structure, even when moving between contexts.

    idi-nat : βˆ€ {Ξ“ Ξ”} {x : Hom Ξ” Cβ‚€} (Οƒ : Hom Ξ“ Ξ”)
            β†’ idi x [ Οƒ ] ≑ idi (x ∘ Οƒ)
    ∘i-nat : βˆ€ {Ξ“ Ξ”} {x y z : Hom Ξ” Cβ‚€}
           β†’ (f : Internal-hom src tgt y z) (g : Internal-hom src tgt x y)
           β†’ (Οƒ : Hom Ξ“ Ξ”) β†’ (f ∘i g) [ Οƒ ] ≑ (f [ Οƒ ] ∘i g [ Οƒ ])

We also provide a bundled definition, letting us talk about arbitrary categories internal to

record Internal-cat : Type (o βŠ” β„“) where
  field
    Cβ‚€ C₁ : Ob
    src tgt : Hom C₁ Cβ‚€
    has-internal-cat : Internal-cat-on src tgt

  open Internal-cat-on has-internal-cat public

  Homi : βˆ€ {Ξ“} (x y : Hom Ξ“ Cβ‚€) β†’ Type β„“
  Homi x y = Internal-hom src tgt x y
  homi : βˆ€ {Ξ“} (f : Hom Ξ“ C₁) β†’ Homi (src ∘ f) (tgt ∘ f)
  homi f .ihom = f
  homi f .has-src = refl
  homi f .has-tgt = refl

  homi-nat : βˆ€ {Ξ“ Ξ”} (f : Hom Ξ” C₁) β†’ (Οƒ : Hom Ξ“ Ξ”)
    β†’ homi f [ Οƒ ] ≑ adjusti (assoc _ _ _) (assoc _ _ _) (homi (f ∘ Οƒ))
  homi-nat f Οƒ = Internal-hom-path refl

-- Some of the naturality conditions required for later definitions will
-- require the use of `PathP`{.agda}, which messes up our equational
-- reasoning machinery. To work around this, we define some custom
-- equational reasoning combinators for working with internal homs.

  casti : βˆ€ {Ξ“} {x x' y y' : Hom Ξ“ Cβ‚€} {f : Homi x y} {g : Homi x' y'}
        β†’ {p p' : x ≑ x'} {q q' : y ≑ y'}
        β†’ PathP (Ξ» i β†’ Homi (p i) (q i)) f g
        β†’ PathP (Ξ» i β†’ Homi (p' i) (q' i)) f g
  casti {Ξ“ = Ξ“} {x} {x'} {y} {y'} {f} {g} {p} {p'} {q} {q'} r =
    transport (Ξ» i β†’
      PathP
        (Ξ» j β†’ Homi (Hom-set Ξ“ Cβ‚€ x x' p p' i j) ( Hom-set Ξ“ Cβ‚€ y y' q q' i j))
        f g) r

  begini_ : βˆ€ {Ξ“} {x x' y y' : Hom Ξ“ Cβ‚€} {f : Homi x y} {g : Homi x' y'}
        β†’ {p p' : x ≑ x'} {q q' : y ≑ y'}
        β†’ PathP (Ξ» i β†’ Homi (p i) (q i)) f g
        β†’ PathP (Ξ» i β†’ Homi (p' i) (q' i)) f g
  begini_ = casti

  _βˆ™i_
    : βˆ€ {Ξ“} {x x' x'' y y' y'' : Hom Ξ“ Cβ‚€}
    β†’ {f : Homi x y} {g : Homi x' y'} {h : Homi x'' y''}
    β†’ {p : x ≑ x'} {q : y ≑ y'} {p' : x' ≑ x''} {q' : y' ≑ y''}
    β†’ PathP (Ξ» i β†’ Homi (p i) (q i)) f g
    β†’ PathP (Ξ» i β†’ Homi (p' i) (q' i)) g h
    β†’ PathP (Ξ» i β†’ Homi ((p βˆ™ p') i) ((q βˆ™ q') i)) f h
  _βˆ™i_ {x = x} {x'} {x''} {y} {y'} {y''} {f} {g} {h} {p} {q} {p'} {q'} r r' i =
    comp (Ξ» j β†’ Homi (βˆ™-filler p p' j i) (βˆ™-filler q q' j i)) (βˆ‚ i) Ξ» where
      j (i = i0) β†’ f
      j (i = i1) β†’ r' j
      j (j = i0) β†’ r i

  ≑i⟨⟩-syntax
    : βˆ€ {Ξ“} {x x' x'' y y' y'' : Hom Ξ“ Cβ‚€}
    β†’ (f : Homi x y) {g : Homi x' y'} {h : Homi x'' y''}
    β†’ {p : x ≑ x'} {q : y ≑ y'} {p' : x' ≑ x''} {q' : y' ≑ y''}
    β†’ PathP (Ξ» i β†’ Homi (p' i) (q' i)) g h
    β†’ PathP (Ξ» i β†’ Homi (p i) (q i)) f g
    β†’ PathP (Ξ» i β†’ Homi ((p βˆ™ p') i) ((q βˆ™ q') i)) f h
  ≑i⟨⟩-syntax f r' r = r βˆ™i r'

  _≑i˘⟨_⟩_
    : βˆ€ {Ξ“} {x x' x'' y y' y'' : Hom Ξ“ Cβ‚€}
    β†’ (f : Homi x y) {g : Homi x' y'} {h : Homi x'' y''}
    β†’ {p : x' ≑ x} {q : y' ≑ y} {p' : x' ≑ x''} {q' : y' ≑ y''}
    β†’ PathP (Ξ» i β†’ Homi (p i) (q i)) g f
    β†’ PathP (Ξ» i β†’ Homi (p' i) (q' i)) g h
    β†’ PathP (Ξ» i β†’ Homi ((sym p βˆ™ p') i) ((sym q βˆ™ q') i)) f h
  _≑i˘⟨_⟩_ f r r'  = symP r βˆ™i r'

  syntax ≑i⟨⟩-syntax f r' r = f ≑i⟨ r ⟩ r'

  infixr 30 _βˆ™i_
  infix 1 begini_
  infixr 2 ≑i⟨⟩-syntax _≑i˘⟨_⟩_

Where did the pullbacks go?πŸ”—

After seeing the definition above, the reader may be slightly concerned: we make no reference to pullbacks, or to limits in at all! How in the world can this be the same as the textbook definition?

The pullbacks in enter the stage when we want to move our internal category structure, which is relative to arbitrary contexts to the smallest possible context. To start, we note that internalizing the identity morphism can be done by looking instantiating idi at the identity morphism.

private module _ (pbs : has-pullbacks C) (β„‚ : Internal-cat) where
  open Internal-cat β„‚
  open Pullbacks C pbs
  open pullback

  internal-id : Hom Cβ‚€ C₁
  internal-id = idi id .ihom

Now let’s see composition: enter, stage rights, the pullbacks. we define to be the object of composable pairs β€” the first pullback square we gave, intersecting on compatible source and target. By translating the (internal) pullback square to (external) indexing, we have a pair of internal morphisms that can be composed.

  Cβ‚‚ : Ob
  Cβ‚‚ = Pb src tgt

  internal-comp : Hom Cβ‚‚ C₁
  internal-comp = (f ∘i g) .ihom where
    f : Homi (src ∘ p₁ src tgt) (tgt ∘ p₁ src tgt)
    f .ihom = p₁ src tgt
    f .has-src = refl
    f .has-tgt = refl

    g : Homi (src ∘ pβ‚‚ src tgt) (src ∘ p₁ src tgt)
    g .ihom = pβ‚‚ src tgt
    g .has-src = refl
    g .has-tgt = sym $ square src tgt

Internal functorsπŸ”—

We will now start our project of relativisng category theory to arbitrary bases. Suppose are internal categories: what are the maps between them? Reasoning diagrammatically, they are the morphisms between object-objects and morphism-objects that preserve source, target, commute with identity, and commute with composition.

Now thinking outside an internal functor consists of a family of maps between internal objects, together with a dependent function between internal morphisms β€” exactly as in the external case! With that indexing, the functoriality constraints also look identical.

  field
    Fiβ‚€ : βˆ€ {Ξ“} β†’ Hom Ξ“ β„‚.Cβ‚€ β†’ Hom Ξ“ 𝔻.Cβ‚€
    Fi₁ : βˆ€ {Ξ“} {x y : Hom Ξ“ β„‚.Cβ‚€} β†’ β„‚.Homi x y β†’ 𝔻.Homi (Fiβ‚€ x) (Fiβ‚€ y)

    Fi-id : βˆ€ {Ξ“} {x : Hom Ξ“ β„‚.Cβ‚€}
          β†’ Fi₁ (β„‚.idi x) ≑ 𝔻.idi (Fiβ‚€ x)
    Fi-∘  : βˆ€ {Ξ“} {x y z : Hom Ξ“ β„‚.Cβ‚€}
          β†’ (f : β„‚.Homi y z) (g : β„‚.Homi x y)
          β†’ Fi₁ (f β„‚.∘i g) ≑ Fi₁ f 𝔻.∘i Fi₁ g

However, do not forget the naturality conditions. Since we now have a β€œdependent function” between internal morphism spaces, its substitution stability depends on stability for the mapping between objects.

    Fiβ‚€-nat : βˆ€ {Ξ“ Ξ”} (x : Hom Ξ” β„‚.Cβ‚€)
            β†’ (Οƒ : Hom Ξ“ Ξ”)
            β†’ Fiβ‚€ x ∘ Οƒ ≑ Fiβ‚€ (x ∘ Οƒ)
    Fi₁-nat : βˆ€ {Ξ“ Ξ”} {x y : Hom Ξ” β„‚.Cβ‚€}
            β†’ (f : β„‚.Homi x y)
            β†’ (Οƒ : Hom Ξ“ Ξ”)
            β†’ PathP (Ξ» i β†’ 𝔻.Homi (Fiβ‚€-nat x Οƒ i) (Fiβ‚€-nat y Οƒ i))
                (Fi₁ f [ Οƒ ]) (Fi₁ (f [ Οƒ ]))

open Internal-functor

Internal functor compositionπŸ”—

As a demonstration of the power of these definitions, we can define composition of internal functors, which β€” at the risk of sounding like a broken record β€” mirrors the external definition exactly.

  _Fi∘_ : Internal-functor 𝔻 𝔼 β†’ Internal-functor β„‚ 𝔻 β†’ Internal-functor β„‚ 𝔼
  (F Fi∘ G) .Fiβ‚€ x = F .Fiβ‚€ (G .Fiβ‚€ x)
  (F Fi∘ G) .Fi₁ f = F .Fi₁ (G .Fi₁ f)
  (F Fi∘ G) .Fi-id = ap (F .Fi₁) (G .Fi-id) βˆ™ F .Fi-id
  (F Fi∘ G) .Fi-∘ f g = ap (F .Fi₁) (G .Fi-∘ f g) βˆ™ F .Fi-∘ _ _
  (F Fi∘ G) .Fiβ‚€-nat x Οƒ = F .Fiβ‚€-nat (G .Fiβ‚€ x) Οƒ βˆ™ ap (F .Fiβ‚€) (G .Fiβ‚€-nat x Οƒ)
  (F Fi∘ G) .Fi₁-nat f Οƒ =
    F .Fi₁-nat (G .Fi₁ f) Οƒ 𝔼.βˆ™i (Ξ» i β†’ F .Fi₁ (G .Fi₁-nat f Οƒ i))

  infixr 30 _Fi∘_

There is also an internal version of the identity functor.

Idi : βˆ€ {β„‚ : Internal-cat} β†’ Internal-functor β„‚ β„‚
Idi .Fiβ‚€ x = x
Idi .Fi₁ f = f
Idi .Fi-id = refl
Idi .Fi-∘ _ _ = refl
Idi .Fiβ‚€-nat _ _ = refl
Idi .Fi₁-nat _ _ = refl

Internal natural transformationsπŸ”—

Internal natural transformations follow the same pattern: we replace objects with generalized objects, morphisms with internal morphisms, and attach a condition encoding stability under substitution. Here again we must state stability over another stability condition.

  field
    Ξ·i : βˆ€ {Ξ“} (x : Hom Ξ“ β„‚.Cβ‚€) β†’ 𝔻.Homi (F .Fiβ‚€ x) (G .Fiβ‚€ x)
    is-naturali : βˆ€ {Ξ“} (x y : Hom Ξ“ β„‚.Cβ‚€) (f : β„‚.Homi x y)
                β†’ Ξ·i y 𝔻.∘i F .Fi₁ f ≑ G .Fi₁ f 𝔻.∘i Ξ·i x
    Ξ·i-nat : βˆ€ {Ξ“ Ξ”} (x : Hom Ξ” β„‚.Cβ‚€)
           β†’ (Οƒ : Hom Ξ“ Ξ”)
           β†’ PathP (Ξ» i β†’ 𝔻.Homi (F .Fiβ‚€-nat x Οƒ i) (G .Fiβ‚€-nat x Οƒ i))
               (Ξ·i x [ Οƒ ]) (Ξ·i (x ∘ Οƒ))

infix 20 _=>i_
open _=>i_

  1. To be clear, we did not draw the identity constraints because they are trivial. Rather, speaking euphemistically, they are highly nontrivial.β†©οΈŽ