module Cat.CartesianClosed.Locally where

Locally cartesian closed categories🔗

A finitely complete category C\mathcal{C} is said to be locally Cartesian closed when each of its slice categories is Cartesian closed. Note that requiring finite limits in C\mathcal{C} does exclude some examples, since C\mathcal{C} might have each of its slices Cartesian closed, but lack a terminal object.1 With the extra condition, a locally Cartesian closed category is Cartesian closed.

record Locally-cartesian-closed {o ℓ} (C : Precategory o ℓ) : Type (o ⊔ ℓ) where
  field
    has-is-lex : Finitely-complete C
    slices-cc  :  A  Cartesian-closed (Slice C A)
      (Slice-products (Finitely-complete.pullbacks has-is-lex))
      Slice-terminal-object

The idea of exponentials in a slice is pretty complicated2, so fortunately, there is an alternative characterisation of local cartesian closure, which is informed by C\mathcal{C}’s internal type theory.

Recall that, when thinking about dependent type theory in a category C\mathcal{C}, we have the following dictionary: The objects of C\mathcal{C} correspond to the contexts Γ,Δ,\Gamma, \Delta, \dots, and the morphisms ΓΔ\Gamma \to \Delta are the substitutions between those contexts. The objects in C/Γ\mathcal{C}/\Gamma are the types in context Γ\Gamma. From this point of view, the pullback functors implement substitution in a dependent type, mapping a type ΓA\Gamma \vdash A to ΔσA\Delta \vdash \sigma^*A, along the substitution σ:ΔΓ\sigma : \Delta \to \Gamma.

To make this a bit clearer, let’s focus on the simplest case, where σ\sigma is the projection of a variable π1:Γ.AΓ\pi_1 : \Gamma.A \to \Gamma. Instantiating the discussion above, we discover that base change along π1\pi_1 will map types ΓB\Gamma \vdash B to their weakenings Γ,x:Aπ1B\Gamma, x : A \vdash \pi_1^*B.

Under this correspondence, what do dependent function types correspond to? Let’s roll up our sleeves and write out some gosh-darn Γ\Gammas and turnstiles. It’s not much, but it’s honest work. We have the introduction and elimination rules

Γ,x:Ae:BΓ(λx.e):Π(x:A)B(x) \frac{ \Gamma, x : A \vdash e : B }{ \Gamma \vdash (\lambda x. e) : \Pi_{(x : A)}B(x) }

Γf:Πx:AB(x)Γe:AΓf(x):B(e) \frac{ \Gamma \vdash f : \Pi_{x : A}B(x) \quad \Gamma \vdash e : A }{ \Gamma \vdash f(x) : B(e) }

which, by abstracting away the substitution of the argument3, expresses that there is an isomorphism between derivations Γf:Π(x:A)B(x)\Gamma \vdash f : \Pi_{(x : A)} B(x) and Γ,x:Af(x):B(x)\Gamma, x : A \vdash f(x) : B(x). If we squint, this says precisely that Π\Pi is a right adjoint to the action of base change along π1:Γ.AA\pi_1 : \Gamma.A \to A!

This is our second characterisation of locally Cartesian closed categories. Generalising away from weakenings, we should have a correspondence between Hom(fA,B)\mathbf{Hom}(f^*A, B) and Hom(A,ΠfB)\mathbf{Hom}(A, \Pi_f B), for an arbitrary substitution f:ΓΔf : \Gamma \to \Delta. Back to categorical language, that is a right adjoint to the base change functor, fitting into an adjoint triple

ΣffΠf. \Sigma_f \dashv f^* \dashv \textstyle\Pi_f\text{.}

From dependent products🔗

But how does this correlate to the characterisation in terms of Cartesian closed slices? Other than the intuition about “function types (in context) between dependent types”, we can do some honest category theory. First, observe that, for f:XAf : X \to A, the product functor ×f:C/AC/A- \times f : \mathcal{C}/A \to \mathcal{C}/A is isomorphically given by

C/AfC/XΣfC/A, \mathcal{C}/A \xrightarrow{f^*} \mathcal{C}/X \xrightarrow{\Sigma_f} \mathcal{C}/A \text{,}

since products in a slice are implemented by pullbacks in C\mathcal{C}; We can chase a g:YAg : Y \to A along the above diagram to see that it first gets sent to fgf^*g as in the diagram

by the pullback functor, then to f(fg):X×AYAf \circ (f^*g) : X \times_A Y \to A by the dependent sum. But this is exactly the object g×fg \times f in C/A\mathcal{C}/A, so that ×f- \times f and Σff\Sigma_f f^* are indeed naturally isomorphic.

    Slice-product-functor :  {X}  make-natural-iso
      (Σf (X .map) F∘ Base-change pullbacks (X .map))
      (Bifunctor.Left ×/ X)

    Slice-product-functor .eta x .map      = id
    Slice-product-functor .eta x .commutes = idr _ ∙ pullbacks _ _ .square
    Slice-product-functor .inv x .map      = id
    Slice-product-functor .inv x .commutes = idr _ ∙ sym (pullbacks _ _ .square)
    Slice-product-functor .eta∘inv x     = ext $ idl _
    Slice-product-functor .inv∘eta x     = ext $ idl _
    Slice-product-functor .natural x y f = ext $ id-comm ∙ ap (id ∘_) (pullbacks _ _ .unique
      (pullbacks _ _ .p₁∘universal) (pullbacks _ _ .p₂∘universal ∙ idl _))

If we then have a functor Πf\Pi_f fitting into an adjoint triple ΣffΠf\Sigma_f \dashv f^* \dashv \Pi_f, we can compose that to obtain ΣffΠff\Sigma_f f^* \dashv \Pi_f f^*, and, by the natural isomorphism we just constructed, ×fΠff- \times f \dashv \Pi_f f^*. Since a right adjoint to Cartesian product is exactly the definition of an exponential object, such an adjoint triple serves to conclude that each slice of C\mathcal{C} is Cartesian closed.

  dependent-product→lcc
    : (Πf    :  {a b} (f : Hom a b)  Functor (Slice C a) (Slice C b))
     (f*⊣Πf :  {a b} (f : Hom a b)  Base-change pullbacks f ⊣ Πf f)
     Locally-cartesian-closed C
  dependent-product→lcc Πf adj = record { has-is-lex = fp ; slices-cc = slice-cc } where
    slice-cc : (A : Ob)  Cartesian-closed (Slice C A) _ _
    slice-cc A = product-adjoint→cartesian-closed (Slice C A) _ _
       f  Πf (f .map) F∘ Base-change pullbacks (f .map))
      λ A  adjoint-natural-isol (to-natural-iso Slice-product-functor)
              (LF⊣GR (adj _) (Σf⊣f* _ _))

Recovering the adjunction🔗

Now suppose that each slice of C\mathcal{C} is Cartesian closed. How do we construct the dependent product Πf:C/AC/B\Pi_f : \mathcal{C}/A \to \mathcal{C}/B? Happily, this is another case where we just have to assemble preëxisting parts, like we’re putting together a theorem from IKEA.

We already know that, since f:ABf : A \to B is an exponentiable object in C/B\mathcal{C}/B, there is a product along ff functor, mapping from the double slice (C/B)/fC/B(\mathcal{C}/B)/f \to \mathcal{C}/B, which is a right adjoint to the constant families functor C/B(C/B)/f\mathcal{C}/B \to (\mathcal{C}/B)/f. And since (by the yoga of iterated slices) (C/B)/f(\mathcal{C}/B)/f is identical to C/A\mathcal{C}/A, this becomes a functor Πf:C/AC/B\Pi_f : \mathcal{C}/A \to \mathcal{C}/B, of the right type.

  lcc→dependent-product
    :  {a b} (f : Hom a b)  Functor (Slice C a) (Slice C b)
  lcc→dependent-product {a} {b} f =
       exponentiable→product _ _ _ _ (has-exp b (cut f)) pullback/
    F∘ Slice-twice f

It remains to verify that it’s actually an adjoint to pullback along ff. We know that it’s a right adjoint to the constant families functor Δf\Delta_f on C/B\mathcal{C}/B, and that constant families are given by π2:g×ff\pi_2 : g \times f \to f. Since the Cartesian product in a slice is given by pullback, the base change functor turns out naturally isomorphic to ff^*, when regarded as a functor C/BC/A\mathcal{C}/B \to \mathcal{C}/A through the equivalence (C/B)/fC/A(\mathcal{C}/B)/f \cong \mathcal{C}/A.

  lcc→pullback⊣dependent-product
    :  {a b} (f : Hom a b)  Base-change pullbacks f ⊣ lcc→dependent-product f
  lcc→pullback⊣dependent-product {b = b} f = adjoint-natural-isol
    (to-natural-iso rem₂) (LF⊣GR rem₁ (Twice⊣Slice f))
    where
    rem₁ : constant-family prod/ ⊣ exponentiable→product (Slice C _) _ _ _ _ _
    rem₁ = exponentiable→constant-family⊣product _ _ _ _ _ _

    rem₂ : make-natural-iso (Twice-slice f F∘ constant-family prod/)
                            (Base-change pullbacks f)
    rem₂ .eta x .map      = id
    rem₂ .eta x .commutes = idr _
    rem₂ .inv x .map      = id
    rem₂ .inv x .commutes = idr _
    rem₂ .eta∘inv x = ext (idr _)
    rem₂ .inv∘eta x = ext (idr _)
    rem₂ .natural x y f = ext $
         idr _
      ·· ap (pullbacks _ _ .universal) prop!
      ·· sym (idl _)

  1. An example is the category of locales and local homeomorphisms, LH\mathbf{LH}. Each slice LH/X\mathbf{LH}/X is Cartesian closed — they’re even topoi — but LH\mathbf{LH} has no terminal object.↩︎

  2. Indeed, even for the category Sets\mathbf{Sets}, showing local Cartesian closure is not at all straightforward: the local exponential fgf^g over BB is the set b:Bf1(b)g1(b)\sum_{b : B} f^{-1}(b) \to g^{-1}(b), though this computation is best understood in terms of slices of sets.↩︎

  3. To expand on the idea of this more categorical application, if we have Γf:Πx:AB(x)\Gamma \vdash f : \Pi_{x : A}B(x), we first “open” it to uncover Γ,x:Af(x):B(x)\Gamma, x : A \vdash f(x) : B(x); if we then have an argument Γa:A\Gamma \vdash a : A, then we can use substitution to obtain Γf(x)[a/x]:B(a)\Gamma \vdash f(x)[a/x] : B(a).↩︎