module Cat.Diagram.Pullback {ℓ ℓ'} (C : Precategory ℓ ℓ') where

Pullbacks🔗

A pullback X×ZYX \times_Z Y of f:XZf : X \to Z and g:YZg : Y \to Z is the product of ff and gg in the category C/Z\mathcal{C}/Z, the category of objects fibred over ZZ. We note that the fibre of X×ZYX \times_Z Y over some element xx of ZZ is the product of the fibres of ff and gg over xx; Hence the pullback is also called the fibred product.

record is-pullback {P} (p₁ : Hom P X) (f : Hom X Z) (p₂ : Hom P Y) (g : Hom Y Z)
  : Type (ℓ ⊔ ℓ') where

  no-eta-equality
  field
    square   : f ∘ p₁ ≡ g ∘ p₂

The concrete incarnation of the abstract nonsense above is that a pullback turns out to be a universal square like the one below. Since it is a product, it comes equipped with projections π1\pi_1 and π2\pi_2 onto its factors; Since isn’t merely a product of XX and YY, but rather of XX and YY considered as objects over ZZ in a specified way, overall square has to commute.

    universal :  {P'} {p₁' : Hom P' X} {p₂' : Hom P' Y}
              f ∘ p₁' ≡ g ∘ p₂'  Hom P' P
    p₁∘universal : {p : f ∘ p₁' ≡ g ∘ p₂'}  p₁ ∘ universal p ≡ p₁'
    p₂∘universal : {p : f ∘ p₁' ≡ g ∘ p₂'}  p₂ ∘ universal p ≡ p₂'

    unique : {p : f ∘ p₁' ≡ g ∘ p₂'} {lim' : Hom P' P}
            p₁ ∘ lim' ≡ p₁'
            p₂ ∘ lim' ≡ p₂'
            lim' ≡ universal p

  unique₂
    : {p : f ∘ p₁' ≡ g ∘ p₂'} {lim' lim'' : Hom P' P}
     p₁ ∘ lim' ≡ p₁'  p₂ ∘ lim' ≡ p₂'
     p₁ ∘ lim'' ≡ p₁'  p₂ ∘ lim'' ≡ p₂'
     lim' ≡ lim''
  unique₂ {p = o} p q r s = unique {p = o} p q ∙ sym (unique r s)

By universal, we mean that any other “square” (here the second “square” has corners PP', XX, YY, ZZ — it’s a bit bent) admits a unique factorisation that passes through PP; We can draw the whole situation as in the diagram below. Note the little corner on PP, indicating that the square is a pullback.

We provide a convenient packaging of the pullback and the projection maps:

record Pullback {X Y Z} (f : Hom X Z) (g : Hom Y Z) : Type (ℓ ⊔ ℓ') where
  no-eta-equality
  field
    {apex} : Ob
    p₁ : Hom apex X
    p₂ : Hom apex Y
    has-is-pb : is-pullback p₁ f p₂ g

  open is-pullback has-is-pb public

Categories with all pullbacks🔗

We also provide a helper module for working with categories that have all pullbacks.

has-pullbacks : Type _
has-pullbacks =  {A B X} (f : Hom A X) (g : Hom B X)  Pullback f g

module Pullbacks (all-pullbacks : has-pullbacks) where
  module pullback {x y z} (f : Hom x z) (g : Hom y z) =
    Pullback (all-pullbacks f g)

  Pb :  {x y z}  Hom x z  Hom y z  Ob
  Pb = pullback.apex

Stability🔗

Pullbacks, in addition to their nature as limits, serve as the way of “changing the base” of a family of objects: if we think of an arrow f:ABf : A \to B as encoding the data of a family over BB (think of the special case where A=Σx:AF(x)A = \Sigma_{x : A} F(x), and f=π1f = \pi_1), then we can think of pulling back ff along g:XBg : X \to B as “the universal solution to making ff a family over XX, via gg”. One way of making this intuition formal is through the fundamental fibration of a category with pullbacks.

In that framing, there is a canonical choice for “the” pullback of an arrow along another: We put the arrow ff we want to pullback on the right side of the diagram, and the pullback is the right arrow. Using the type is-pullback defined above, the arrow which results from pulling back is adjacent to the adjustment: is-pullback f⁺ g _ f. To help keep this straight, we define what it means for a class of arrows to be stable under pullback: If f has a given property, then so does f⁺, for any pullback of f.

is-pullback-stable
  :  {ℓ'}  (∀ {a b}  Hom a b  Type ℓ')  Type _
is-pullback-stable P =
   {p A B X} (f : Hom A B) (g : Hom X B) {f⁺ : Hom p X} {p2}
   P f  is-pullback f⁺ g p2 f  P f⁺