index

⚠️ This module is not part of this project and is only included for reference.
It is either part of the 1lab, the cubical library, or a built-in Agda module.

<!--
```agda
open import 1Lab.Type.Sigma
open import 1Lab.HLevel
open import 1Lab.Equiv
open import 1Lab.Path
open import 1Lab.Type
```
-->

```agda
module 1Lab.Univalence where
```

# Univalence {defines=univalence}

In Homotopy Type Theory, **univalence** is the principle stating that
[equivalent] types can be [identified]. When [the book] first came out,
there was no widely-accepted _computational_ interpretation of this
principle, so it was added to the theory as an axiom: the **univalence
axiom**.

[the book]: https://homotopytypetheory.org/book
[equivalent]: 1Lab.Equiv.html#_≃_
[identified]: Prim.Kan.html#Path

Precisely, the axiom as presented in the book consists of the following
data (right under remark §2.10.4):

* A map which turns equivalences into identifications of types.
This map is called `ua`{.Agda}.

* A rule for eliminating identifications of types, by turning them into
equivalences: `path→equiv`{.Agda}

* The propositional computation rule, stating that transport along
`ua(f)` is identical to applying `f`: `uaβ`{.Agda}.

In the book, there is an extra postulated datum asserting that
`ua`{.Agda} is an inverse to `path→equiv`{.Agda}. This datum does not
have a name in this development, because it's proved in-line in the
construction of the term `univalence`{.Agda}.

The point of cubical type theory is to give these terms constructive
interpretations, i.e., make them definable in the theory, in terms of
constructions that have computational behaviour. Let's see how this is
done.

## Glue {defines="glueing glue-type"}

To even _state_ univalence, we first have to make sure that the concept
of “paths between types” makes sense in the first place. In “Book HoTT”,
paths between types are a well-formed concept because the path type is
uniformly inductively defined for _everything_ --- including universes.
This is not the case in Cubical type theory, where for paths in $T$ to
be well-behaved, $T$ must be _fibrant_.

Since there's no obvious choice for how to interpret `hcomp`{.Agda} in
`Type`{.Agda}, a fine solution is to make `hcomp`{.Agda} its own type
former. This is the approach taken by some Cubical type theories in the
[RedPRL school]. Univalence in those type theories is then achieved by
adding a type former, called `V`, which turns an equivalence into a
path.

[RedPRL school]: https://redprl.org/

[_equivalences_]: 1Lab.Equiv.html#is-equiv
[open box]: 1Lab.Path.html#transitivity

In [CCHM] --- and therefore Cubical Agda --- a different approach is
taken, which combines proving univalence with defining a fibrancy
structure for the universe. The core idea is to define a new type
former, `Glue`{.Agda}, which "glues" a [partial type], along an
equivalence, to a total type.

[CCHM]: https://arxiv.org/abs/1611.02108
[partial type]: 1Lab.Path.html#partial-elements

<!--
```agda
private
  variable
     ℓ' : Level

  primitive
    primGlue : (A : Type ) {φ : I}
              (T : Partial φ (Type ℓ'))  (e : PartialP φ  o  T o  A))
              Type ℓ'

    prim^glue : {A : Type } {φ : I}
               {T : Partial φ (Type ℓ')}  {e : PartialP φ  o  T o  A)}
               PartialP φ T  A  primGlue A T e

    prim^unglue : {A : Type } {φ : I}
                 {T : Partial φ (Type ℓ')}  {e : PartialP φ  o  T o  A)}
                 primGlue A T e  A

open import Prim.HCompU
open import 1Lab.Equiv.FromPath
```
-->

```agda
Glue : (A : Type )
      {φ : I}
      (Te : Partial φ (Σ[ T  Type ℓ' ] (T  A)))
      Type ℓ'
```

The public interface of `Glue`{.Agda} demands a type $A$, called the
_base type_, a formula $\varphi$, and a [partial type] $T$ which is
equivalent to $A$. Since the equivalence is defined _inside_ the partial
element, it can also (potentially) vary over the interval, so in reality
we have a _family_ of partial types $T$ and a _family_ of partial
equivalences $T \simeq A$.

In the specific case where we set $\varphi = \neg i \lor i$, we can
illustrate `Glue A (T, f)` as the dashed line in the square diagram
below. The conceptual idea is that by "gluing" $T$ onto a totally
defined type, we get a type which [extends] $T$.

[extends]: 1Lab.Path.html#extensibility

~~~{.quiver}
\[\begin{tikzcd}
  {T(i0)} && {T(i1)} \\
  \\
  {A(i0)} && {A(i1)}
  \arrow[dashed, from=1-1, to=1-3]
  \arrow["A", from=3-1, to=3-3]
  \arrow["{f(i1)}"{description}, from=1-3, to=3-3]
  \arrow["{f(i0)}"{description}, from=1-1, to=3-1]
\end{tikzcd}\]
~~~

<!--
```agda
Glue A Te = primGlue A  x  Te x .fst)  x  Te x .snd)

unglue
  : {A : Type } (φ : I) {T : Partial φ (Type ℓ')}
    {e : PartialP φ  o  T o  A)}
   primGlue A T e  A
unglue φ = prim^unglue {φ = φ}

glue-inc
  : {A : Type } (φ : I)
   {Tf : Partial φ (Σ[ B  Type ℓ' ] B  A)}
   (p : PartialP φ  { (φ = i1)  Tf 1=1 .fst }))
   A [ φ   { (φ = i1)  Tf 1=1 .snd .fst (p 1=1) }) ]
   Glue A Tf
glue-inc φ p x = prim^glue {φ = φ} p (outS x)

```
-->

[family of partial types]: 1Lab.Path.html#partial-elements

For `Glue`{.Agda} to extend $T$, we add a computation rule which could
be called a **boundary condition**, since it specifies how `Glue`{.Agda}
behaves on the boundaries of cubes. Concisely, when $\varphi = i1$, we
have that `Glue`{.Agda} evaluates to the partial type. This is exactly
what it means for `Glue`{.Agda} to extend $T$!

```agda
module _ {A B : Type} {e : A  B} where
  private
    Glue-boundary : Glue B {i1}  x  A , e)  A
    Glue-boundary i = A
```

Furthermore, since we can turn any family of paths into a family of
equivalences, we can use the `Glue`{.Agda} construct to implement
something with precisely the same interface as `hcomp`{.Agda} for
`Type`{.Agda}:

```agda
glue-hfill
  :  {} φ (u :  i  Partial (φ  ~ i) (Type ))
    i  Type  [ _   { (i = i0)  u i0 1=1
                          ; (φ = i1)  u i 1=1 }) ]
```

The type of `glue-hfill`{.Agda} is the same as that of `hfill`{.Agda},
but the type is stated much more verbosely --- so that we may define it
without previous reference to a `hcomp`{.Agda} analogue. Like
`hfill`{.Agda}, `glue-hfill`{.Agda} extends an open box of types to a
totally-defined cube. The type of `glue-hfill`{.Agda} expresses this in
terms of extensions: We have a path (that's the `∀ i →` binder) of
`Type`{.Agda}s which agrees with `outS u0` on the left endpoint, and
with `u` everywhere.

```agda
glue-hfill φ u i = inS (
  Glue (u i0 1=1) {φ = φ  ~ i}
    λ { (φ = i1)  u i 1=1 , line→equiv  j  u (i  ~ j) 1=1)
      ; (i = i0)  u i0 1=1 , line→equiv  i  u i0 1=1)
      })
```

In the case for  $i = \rm{i0}$, we must glue $u0$ onto itself using the
identity equivalence. This guarantees that the boundary of the stated
type for `glue-hfill`{.Agda} is satisfied. However, since different
faces of partial elements must agree where they are defined, we can not
use the identity equivalence directly, since `line→equiv refl` is not
definitionally the identity equivalence.

When $\varphi = \rm{\phi}$, hence where $u$ is defined, we glue the
endpoint $u$ onto $u0$ using the equivalence generated by the path
provided by $u$ itself! It's a family of partial paths, after all, and
that can be turned into a family of partial equivalences.

Using `hcomp-unique`{.Agda} and `glue-hfill`{.Agda} together, we get a
internal characterisation of the fibrancy structure of the universe.
While `hcomp-unique`{.Agda} may appear surprising, it is essentially a
generalisation of the uniqueness of path compositions: Any open box has
a contractible space of fillers.

```agda
hcomp≡Glue :  {} {φ} (u :  i  Partial (φ  ~ i) (Type ))
            hcomp φ u
            Glue (u i0 1=1)
               { (φ = i1)  u i1 1=1 , line→equiv  j  u (~ j) 1=1) })
hcomp≡Glue {φ = φ} u = hcomp-unique φ u (glue-hfill φ u)
```

## Paths from Glue

Since `Glue`{.Agda} generalises `hcomp`{.Agda} by allowing a partial
equivalence as its “tube”, rather than a partial path, it allows us to
turn any equivalence into a path, using a sort of "trick": We consider
the _line_ with endpoints $A$ and $B$ as an open cube to be filled. A
filler for this line is exactly a path $A \equiv B$. Since `Glue`{.Agda}
fills open boxes of types using equivalences, this path exists!

```agda
ua : {A B : Type }  A  B  A  B
ua {A = A} {B} eqv i = Glue B λ { (i = i0)  A , eqv
                                ; (i = i1)  B , _ , id-equiv
                                }
```

Semantically, the explanation of `ua`{.Agda} as completing a partial
line is sufficient. But we can also ask ourselves: Why does this
definition go through, _syntactically_? Because of the boundary
condition for Glue: when `i = i0`, the whole thing evaluates to `A`,
meaning that the left endpoint of the path is correct. The same thing
happens with the right endpoint.

The action of [transporting] along `ua(f)` can be described by chasing
an element around the diagram that illustrates Glue in the $\varphi = i
\lor \neg i$ case, specialising to `ua`{.Agda}. Keep in mind that, since
the right face of the diagram "points in the wrong direction", it must
be inverted. However, the inverse of the identity equivalence is the
identity equivalence, so nothing changes (for this example).

[transporting]: 1Lab.Path.html

<figure>
~~~{.quiver}
\[\begin{tikzcd}
  A && B \\
  \\
  B && B
  \arrow["{\refl}"', from=3-1, to=3-3]
  \arrow["f"', "\text{\rotatebox[origin=c]{90}{$\sim$}}", from=1-1, to=3-1]
  \arrow["{\id}", "\text{\rotatebox[origin=c]{270}{$\sim$}}"', from=1-3, to=3-3]
  \arrow["{\rm{ua}(f)}", dashed, from=1-1, to=1-3]
\end{tikzcd}\]
~~~
</figure>

1. The action that corresponds to the left face of the diagram is to
apply the underlying function of `f`. This contributes the `f .fst x`
part of the `uaβ`{.Agda} term below.

[a pair]: 1Lab.Equiv.html#_≃_

2. For the bottom face, we have a path rather than an equivalence, so we
must `transport`{.Agda} along it. In this case, the path is the
reflexivity on `B`, but in a more general `Glue`{.Agda} construction, it
might be a non-trivial path.

  To compensate for this extra transport, we use `coe1→i`{.Agda}, which
  connects `f .fst x` and `transport (λ i → B) (f .fst x)`.

3. Finally, we apply the inverse of the identity equivalence,
corresponding to the right face in the diagram. This immediately
computes away, and thus contributes nothing to the `uaβ`{.Agda} path.

```agda
uaβ : {A B : Type } (f : A  B) (x : A)  transport (ua f) x  f .fst x
uaβ {A = A} {B} f x i = coe1→i  _  B) i (f .fst x)
```

Since `ua`{.Agda} is a map that turns equivalences into paths, we can
compose it with a function that turns [isomorphisms] into equivalences
to get the map `Iso→Path`{.Agda}.

[isomorphisms]: 1Lab.Equiv.html#Iso

```agda
Iso→Path : {A B : Type }  Iso A B  A  B
Iso→Path (f , iiso) = ua (f , is-iso→is-equiv iiso)
```

## Paths over ua

The introduction and elimination forms for `Glue`{.Agda} can be
specialised to the case of `ua`{.Agda}, leading to the definitions of
`ua-glue`{.Agda} and `ua-unglue`{.Agda} below. Their types are written
in terms of interval variables and [extensions], rather than using
`Path`{.Agda}s, because these typings make the structure of
`Glue`{.Agda} more explicit.

[extensions]: 1Lab.Path.html#extensibility

The first, `ua-unglue`{.Agda}, tells us that if we have some `x : ua e
i` (varying over an interval variable `i`), then we have an element of
`B` which agrees with `e .fst x` on the left and with `x` on the right.

```agda
ua-unglue :  {A B : Type } (e : A  B) (i : I) (x : ua e i)  B
ua-unglue e i x = unglue (i  ~ i) x
```

We can factor the interval variable out, to get a type in terms of
`PathP`{.Agda}, leading to an explanation of `ua-unglue` without
mentioning extensions: A path `x ≡ y` over `ua e` induces a path `e .fst
x ≡ y`.

```agda
ua-pathp→path :  {A B : Type } (e : A  B) {x : A} {y : B}
               PathP  i  ua e i) x y
               e .fst x  y
ua-pathp→path e p i = ua-unglue e i (p i)
```

In the other direction, we have `ua-glue`{.Agda}, which expresses that a
path `e .fst x ≡ y` implies that `x ≡ y` over `ua e`. For the type of
`ua-glue`{.Agda}, suppose that we have a partial element $x$ defined on
the left endpoint of the interval, together with an extension $y$ of
$e(x)$ where $x$ is defined. What `ua-glue`{.Agda} expresses is that we
can complete this to a path in $\rm{ua}(e)$, which agrees with $x$ and
$y$ where these are defined.

```agda
ua-glue :  {A B : Type } (e : A  B) (i : I)
            (x : Partial (~ i) A)
            (y : B [ _   { (i = i0)  e .fst (x 1=1) }) ])
           ua e i [ _   { (i = i0)  x 1=1
                            ; (i = i1)  outS y
                            }) ]
ua-glue e i x y = inS (prim^glue {φ = i  ~ i}
                                  { (i = i0)  x 1=1
                                    ; (i = i1)  outS y })
                                 (outS y))
```

Observe that, since $y$ is partially in the image of $x$, this
essentially constrains $x$ to be a “partial preimage” of $y$ under the
equivalence $e$. Factoring in the type of the interval, we get the
promised map between dependent paths over `ua`{.Agda} and paths in B.

```agda
path→ua-pathp :  {A B : Type } (e : A  B) {x : A} {y : B}
               e .fst x  y
               PathP  i  ua e i) x y
path→ua-pathp e {x = x} p i = outS (ua-glue e i  { (i = i0)  x }) (inS (p i)))
```

The "pathp to path" versions of the above lemmas are definitionally
inverses, so they provide a characterisation of `PathP (ua f)` in terms
of non-dependent paths.

```agda
ua-pathp≃path :  {A B : Type } (e : A  B) {x : A} {y : B}
               (e .fst x  y)  (PathP  i  ua e i) x y)
ua-pathp≃path eqv .fst = path→ua-pathp eqv
ua-pathp≃path eqv .snd .is-eqv y .centre = strict-fibres (ua-pathp→path eqv) y .fst
ua-pathp≃path eqv .snd .is-eqv y .paths = strict-fibres (ua-pathp→path eqv) y .snd
```

# The “axiom” {defines=univalence-axiom}

The actual “univalence axiom”, as stated in the HoTT book, says that the
canonical map `A ≡ B`, defined using `J`{.Agda}, is an equivalence. This
map is `id→equiv`{.Agda}, defined right above. In more intuitive terms,
it's "casting" the identity equivalence `A ≃ A` along a proof that `A ≡
B` to get an equivalence `A ≃ B`.

```agda
module _ where private
  id→equiv : {A B : Type }  A  B  A  B
  id→equiv {A = A} {B} = J  x _  A  x) (_ , id-equiv)

  id→equiv-refl : {A : Type }  id→equiv  i  A)  (_ , id-equiv)
  id→equiv-refl {A = A} = J-refl  x _  A  x) (_ , id-equiv)
```

However, because of efficiency concerns (Agda _is_ a programming
language, after all), instead of using `id→equiv`{.Agda} defined using
J, we use `path→equiv`{.Agda}, which is [defined in an auxiliary
module](1Lab.Equiv.FromPath.html).

```agda
path→equiv : {A B : Type }  A  B  A  B
path→equiv p = line→equiv  i  p i)
```

Since identity of equivalences is determined by identity of their
underlying functions, to show that `path→equiv`{.Agda} of `refl`{.Agda}
is the identity equivalence, we use `coe1→i`{.Agda} to show that
`transport`{.Agda} by `refl`{.Agda} is the identity.

```agda
path→equiv-refl : {A : Type }  path→equiv (refl {x = A})  (id , id-equiv)
path→equiv-refl {A = A} =
  Σ-path  i x  coe1→i  i  A) i x)
         (is-prop→pathp  i  is-equiv-is-prop _) _ _)
```

For the other direction, we must show that `ua`{.Agda} of
`id-equiv`{.Agda} is `refl`{.Agda}. We can do this quite efficiently
using `Glue`{.Agda}. Since this is a path between paths, we have two
interval variables.

```agda
ua-id-equiv : {A : Type }  ua (_ , id-equiv {A = A})  refl
ua-id-equiv {A = A} i j = Glue A {φ = i  ~ j  j}  _  A , _ , id-equiv)
```

We can then prove that the map `path→equiv`{.Agda} is an isomorphism,
hence an equivalence. It's very useful to have explicit names for the
proofs that `path→equiv`{.Agda} and `ua`{.Agda} are equivalences
without referring to components of `Path≃Equiv`{.Agda}, so we
introduce names for them as well.

```agda
Path≃Equiv   : {A B : Type }  Iso (A  B) (A  B)
univalence   : {A B : Type }  is-equiv (path→equiv {A = A} {B})
univalence⁻¹ : {A B : Type }  is-equiv (ua {A = A} {B})

Path≃Equiv {A = A} {B = B} = path→equiv , iiso where
  iiso : is-iso path→equiv
  iiso .is-iso.inv = ua
```

We show that `path→equiv` inverts `ua`{.Agda}, which means proving that
one can recover the original equivalence from the generated path.
Because of the computational nature of Cubical Agda, all we have to do
is apply `uaβ`{.Agda}:

```agda
  iiso .is-iso.rinv (f , is-eqv) =
    Σ-path (funext (uaβ (f , is-eqv))) (is-equiv-is-prop f _ _)
```

For the other direction, we use [path induction] to reduce the problem
from showing that `ua`{.Agda} inverts `path→equiv`{.Agda} for an
arbitrary path (which is hard) to showing that `path→equiv`{.Agda}
takes `refl`{.Agda} to the identity equivalence
(`path→equiv-refl`{.Agda}), and that `ua`{.Agda} takes the identity
equivalence to `refl`{.Agda} (`ua-id-equiv`{.Agda}).

[path induction]: 1Lab.Path.html#J

```agda
  iiso .is-iso.linv =
    J  _ p  ua (path→equiv p)  p)
      (ap ua path→equiv-refl  ua-id-equiv)

univalence {A = A} {B} = is-iso→is-equiv (Path≃Equiv .snd)
univalence⁻¹ {A = A} {B} = is-iso→is-equiv (is-iso.inverse (Path≃Equiv .snd))
```

In some situations, it is helpful to have a proof that
`path→equiv`{.Agda} followed by `an adjustment of levels`{.Agda
ident=lift} is still an equivalence:

```agda
univalence-lift : {A B : Type }  is-equiv  e  lift (path→equiv {A = A} {B} e))
univalence-lift { = } = is-iso→is-equiv morp where
  morp : is-iso  e  lift { = lsuc } (path→equiv e))
  morp .is-iso.inv x = ua (x .Lift.lower)
  morp .is-iso.rinv x =
    lift (path→equiv (ua (x .Lift.lower))) ≡⟨ ap lift (Path≃Equiv .snd .is-iso.rinv _) 
    x                                      
  morp .is-iso.linv x = Path≃Equiv .snd .is-iso.linv _
```

## Equivalence induction {defines="equivalence-induction"}

One useful consequence of $(A \equiv B) \simeq (A \simeq B)$[^2] is that
the type of _equivalences_ satisfies [the same induction principle] as
the type of _identifications_. By analogy with how path induction can be
characterised as contractibility of singletons and transport,
“equivalence induction” can be characterised as transport and
contractibility of _singletons up to equivalence_:

[the same induction principle]: 1Lab.Path.html#J

```agda
Equiv-is-contr :  {} (A : Type )  is-contr (Σ[ B  Type  ] A  B)
is-contr.centre (Equiv-is-contr A)            = A , _ , id-equiv
is-contr.paths (Equiv-is-contr A) (B , A≃B) i = ua A≃B i , p i , q i where
  p : PathP  i  A  ua A≃B i) id (A≃B .fst)
  p i x = outS (ua-glue A≃B i  { (i = i0)  x }) (inS (A≃B .fst x)))

  q : PathP  i  is-equiv (p i)) id-equiv (A≃B .snd)
  q = is-prop→pathp  i  is-equiv-is-prop (p i)) _ _
```

Combining `Equiv-is-contr`{.Agda} with `subst`{.Agda}, we get an induction
principle for the type of equivalences based at $A$: To prove $P(B,e)$
for any $e : A \simeq B$, it suffices to consider the case where $B$ is
$A$ and $e$ is the identity equivalence.

```agda
EquivJ :  { ℓ'} {A : Type }
        (P : (B : Type )  A  B  Type ℓ')
        P A (_ , id-equiv)
        {B : Type } (e : A  B)
        P B e
EquivJ P pid eqv =
  subst  e  P (e .fst) (e .snd)) (Equiv-is-contr _ .is-contr.paths (_ , eqv)) pid
```

[^2]: Not the fundamental theorem of engineering!

Equivalence induction simplifies the proofs of many properties about
equivalences. For example, if $f$ is an equivalence, then so is its
`action on paths`{.Agda ident=ap} $\ap(f)$.

```agda
private
  is-equiv→is-embedding :  {} {A B : Type }
                         (f : A  B)  is-equiv f
                         {x y : A}
                         is-equiv (ap f {x = x} {y = y})
  is-equiv→is-embedding f eqv =
    EquivJ  B e  is-equiv (ap (e .fst))) id-equiv (f , eqv)
```

The proof can be rendered in English roughly as follows:

> Suppose $f : A \to B$ `is an equivalence`{.Agda ident=is-equiv}. We
want to show that, for any choice of $x, y : A$, the map
$\ap(f)_{x,y} : x \equiv y \to f(x) \equiv f(y)$ is an equivalence.
>
> By `induction`{.Agda ident=EquivJ}, it suffices to cover the case
where $B$ is $A$, and $f$ is the identity function.
>
> But then, we have that $\ap(\id)$ is definitionally equal to
$\id$, which is known to be `an equivalence`{.Agda ident=id-equiv}.
<span class=qed>$\blacksquare$</span>

## Object classifiers {defines="object-classifier"}

In category theory, the idea of _classifiers_ (or _classifying objects_)
often comes up when categories applied to the study of logic. For
example, any [elementary topos] has a _[subobject classifier]_: an
object $\Omega$ such that maps $B \to \Omega$ corresponds to maps $A \to
B$ with propositional fibres (equivalently, inclusions $A
\hookrightarrow B$). In higher categorical analyses of logic,
classifying objects exist for more maps: an elementary
**2**-topos has a [discrete object classifier], which classify maps with
_discrete_ fibres.

[elementary topos]: https://ncatlab.org/nlab/show/topos#ElementaryTopos
[subobject classifier]: https://ncatlab.org/nlab/show/subobject+classifier
[discrete object classifier]: https://ncatlab.org/nlab/show/discrete+object+classifier

Since a $(1,1)$-topos has classifiers for maps with $(-1)$-truncated
fibres, and a $(2,1)$-topos has classifiers for maps with $0$-truncated
fibres, one might expect that an [$\io$-topos] would have
classifiers for maps with fibres that are not truncated at all. This is
indeed the case! In HoTT, this fact is internalised using the univalent
universes, and we can prove that univalent universes are [_object
classifiers_].

[$\io$-topos]: https://ncatlab.org/nlab/show/(infinity,1)-topos
[_object classifiers_]: https://ncatlab.org/nlab/show/object+classifier

<!--
```agda
private variable
  A B E : Type 
open is-iso
```
-->

As an intermediate step, we prove that the value $B(a)$ of a type family
$B$ at a point $a$ is equivalent to the fibre of $\rm{fst} : \Sigma_{(x
: A)}B(x) \to A$ over $a$. The proof follows from the De Morgan
structure on the interval, and the “spread” operation `coe1→i`{.Agda}.

```agda
-- HoTT book lemma 4.8.1
Fibre-equiv : (B : A  Type ℓ') (a : A)
             fibre (fst {B = B}) a  B a
Fibre-equiv B a = Iso→Equiv isom where
  isom : Iso _ _
  isom .fst ((x , y) , p) = subst B p y
  isom .snd .inv x        = (a , x) , refl
  isom .snd .rinv x i     = coe1→i  _  B a) i x
  isom .snd .linv ((x , y) , p) i =
    (p (~ i) , coe1→i  j  B (p (~ i  ~ j))) i y) , λ j  p (~ i  j)
```

Another fact from homotopy theory that we can import into homotopy
_type_ theory is that any map is equivalent to a fibration. More
specifically, given a map $p : E \to B$, the total space $E$ is
equivalent to the dependent sum of the fibres. The theorems
`Total-equiv`{.Agda} and `Fibre-equiv`{.Agda} are what justify referring
to `Σ`{.Agda} the "total space" of a type family.

```agda
Total-equiv : (p : E  B)  E  Σ B (fibre p)
Total-equiv p = Iso→Equiv isom where
  isom : Iso _ (Σ _ (fibre p))
  isom .fst x                   = p x , x , refl
  isom .snd .inv (_ , x , _)    = x
  isom .snd .rinv (b , x , q) i = q i , x , λ j  q (i  j)
  isom .snd .linv x             = refl
```

Putting these together, we get the promised theorem: The space of maps
$B \to \ty$ is equivalent to the space of fibrations with base
space $B$ and variable total space $E$, $\Sigma_{(E : \ty)}
(E \to B)$. If we allow $E$ and $B$ to live in different universes, then
the maps are classified by the biggest universe in which they both fit,
namely `Type (ℓ ⊔ ℓ')`. Note that the proof of `Fibration-equiv`{.Agda}
makes fundamental use of `ua`{.Agda}, to construct the witnesses that
taking fibres and taking total spaces are inverses. Without `ua`{.Agda},
we could only get an "isomorphism-up-to-equivalence" of types.

```agda
Fibration-equiv :  { ℓ'} {B : Type }
                 (Σ[ E  Type (  ℓ') ] (E  B))
                 (B  Type (  ℓ'))
Fibration-equiv {B = B} = Iso→Equiv isom where
  isom : Iso (Σ[ E  Type _ ] (E  B)) (B  Type _)
  isom .fst (E , p)       = fibre p
  isom .snd .inv p⁻¹      = Σ _ p⁻¹ , fst
  isom .snd .rinv prep i x = ua (Fibre-equiv prep x) i
  isom .snd .linv (E , p) i
    = ua e (~ i) , λ x  fst (ua-unglue e (~ i) x)
    where e = Total-equiv p
```

To solidify the explanation that object classifiers generalise the
$(n-2)$-truncated object classifiers you would find in a $(n,1)$-topos,
we prove that any class of maps described by a property $P$ which holds
of all of its fibres (or even _structure_ on all of its fibres!) has a
classifying object --- the total space $\Sigma P$.

For instance, if we take $P$ to be the property of `being a
proposition`{.Agda ident=is-prop}, this theorem tells us that `Σ is-prop`
classifies _subobjects_. With the slight caveat that `Σ is-prop` is not
closed under impredicative quantification, this corresponds exactly to
the notion of subobject classifier in a $(1,1)$-topos, since the maps
with propositional fibres are precisely the injective maps.

<!--
```
_ = is-prop
```
-->

:::{.definition #map-classifier}
Since the type of "maps into B with variable domain and P fibres" has a
very unwieldy description --- both in words or in Agda syntax --- we
abbreviate it by $\ell /_{[P]} B$. The notation is meant to evoke the
idea of a slice category: The objects of $C/c$ are objects of the
category $C$ equipped with choices of maps into $c$. Similarly, the
objects of $\ell/_{[P]}B$ are objects of the universe $\ty\
\ell$, with a choice of map $f$ into $B$, such that $P$ holds for all
the fibres of $f$.
:::

```agda
_/[_]_ :  {ℓ' ℓ''} ( : Level)  (Type (  ℓ')  Type ℓ'')  Type ℓ'  Type _
_/[_]_ {} ℓ' P B =
  Σ (Type (  ℓ')) λ A 
  Σ (A  B) λ f 
  (x : B)  P (fibre f x)
```

The proof that the slice $\ell /_{[P]} B$ is classified by $\Sigma P$
follows from elementary properties of $\Sigma$ types (namely:
`reassociation`{.Agda ident=Σ-assoc}, `distributivity`{.Agda
ident=Σ-Π-distrib} of $\Sigma$ over $\Pi$), and the classification
theorem `Fibration-equiv`{.Agda}. Really, the most complicated part of
this proof is rearranging the nested sum and product types to a form to
which we can apply `Fibration-equiv`{.Agda}.

```agda
Map-classifier
  :  { ℓ' ℓ''} {B : Type ℓ'} (P : Type (  ℓ')  Type ℓ'')
   ( /[ P ] B)  (B  Σ _ P)
Map-classifier { = } {B = B} P =
  (Σ _ λ A  Σ _ λ f  (x : B)  P (fibre f x))     ≃⟨ Σ-assoc 
  (Σ _ λ { (x , f)  (x : B)  P (fibre f x) })   ≃⟨ Σ-ap-fst (Fibration-equiv {ℓ' = }) 
  (Σ _ λ A  (x : B)  P (A x))                   ≃⟨ Σ-Π-distrib e⁻¹ 
  (B  Σ _ P)                                     ≃∎
```

<!--
```agda
module ua {} {A B : Type } = Equiv (ua {A = A} {B} , univalence⁻¹)

unglue-is-equiv
  :  { ℓ'} {A : Type } (φ : I)
   {B : Partial φ (Σ (Type ℓ') (_≃ A))}
   is-equiv {A = Glue A B} (unglue φ)
unglue-is-equiv {A = A} φ {B = B} .is-eqv y = extend→is-contr ctr
  where module _ (ψ : I) (par : Partial ψ (fibre (unglue φ) y)) where
    fib : .(p : IsOne φ)
         fibre (B p .snd .fst) y
          [ (ψ  φ)   { (ψ = i1) (φ = i1)  par 1=1 }) ]
    fib p = is-contr→extend (B p .snd .snd .is-eqv y) (ψ  φ) _

    sys :  j  Partial (φ  ψ  ~ j) A
    sys j (j = i0) = y
    sys j (φ = i1) = outS (fib 1=1) .snd (~ j)
    sys j (ψ = i1) = par 1=1 .snd (~ j)

    ctr = inS $ₛ glue-inc φ {Tf = B}  { (φ = i1)  outS (fib 1=1) .fst })
                  (inS (hcomp (φ  ψ) sys))
               ,  i  hfill (φ  ψ) (~ i) sys)

ua-unglue-is-equiv
  :  {} {A B : Type } (f : A  B)
   PathP  i  is-equiv (ua-unglue f i)) (f .snd) id-equiv
ua-unglue-is-equiv f =
  is-prop→pathp  j  is-equiv-is-prop (ua-unglue f j)) (f .snd) id-equiv

ua∙ :  {} {A B C : Type } {f : A  B} {g : B  C}  ua (f ∙e g)  ua f  ua g
ua∙ { = } {A} {B} {C} {f} {g} = ∙-unique (ua (f ∙e g)) λ i j  Glue C λ where
  (i = i0)  ua f j ,  x  g .fst (ua-unglue f j x)) ,
    is-prop→pathp  j  is-equiv-is-prop  x  g .fst (ua-unglue f j x)))
      ((f ∙e g) .snd) (g .snd) j
  (i = i1)  ua (f ∙e g) j , ua-unglue (f ∙e g) j , ua-unglue-is-equiv (f ∙e g) j
  (j = i0)  A , f ∙e g
  (j = i1)  ua g i , ua-unglue g i , ua-unglue-is-equiv g i

sym-ua :  {} {A B : Type } (e : A  B)  sym (ua e)  ua (e e⁻¹)
sym-ua {A = A} {B = B} e i j = Glue B λ where
  (i = i0)  ua e (~ j)   , ua-unglue e (~ j) , ua-unglue-is-equiv e (~ j)
  (i = i1)  ua (e e⁻¹) j ,  x  e .fst (ua-unglue (e e⁻¹) j x)) ,
    is-prop→pathp  j  is-equiv-is-prop λ x  e .fst (ua-unglue (e e⁻¹) j x))
      (((e e⁻¹) ∙e e) .snd) (e .snd) j
  (j = i0)  B ,  x  Equiv.ε e x (~ i)) ,
    is-prop→pathp  j  is-equiv-is-prop λ x  Equiv.ε e x (~ j))
      id-equiv (((e e⁻¹) ∙e e) .snd) i
  (j = i1)  A , e

ua→ :  { ℓ'} {A₀ A₁ : Type } {e : A₀  A₁} {B : (i : I)  Type ℓ'}
  {f₀ : A₀  B i0} {f₁ : A₁  B i1}
   ((a : A₀)  PathP B (f₀ a) (f₁ (e .fst a)))
   PathP  i  ua e i  B i) f₀ f₁
ua→ {B = B} {f₀ = f₀} {f₁} h i a =
  comp  j  B (i  ~ j)) ( i) λ where
    j (j = i0)  f₁ (unglue ( i) a)
    j (i = i0)  h a (~ j)
    j (i = i1)  f₁ a

ua→2 :  { ℓ' ℓ''} {A₀ A₁ : Type } {e₁ : A₀  A₁}
  {B₀ B₁ : Type ℓ'} {e₂ : B₀  B₁}
  {C : (i : I)  Type ℓ''}
  {f₀ : A₀  B₀  C i0} {f₁ : A₁  B₁  C i1}
   (∀ a b  PathP C (f₀ a b) (f₁ (e₁ .fst a) (e₂ .fst b)))
   PathP  i  ua e₁ i  ua e₂ i  C i) f₀ f₁
ua→2 h = ua→ (ua→  h)

transport-∙ :  {} {A B C : Type }
             (p : A  B) (q : B  C) (u : A)
             transport (p  q) u  transport q (transport p u)
transport-∙ p q x i =
  transport (∙-filler' p q (~ i)) (transport-filler-ext p i x)

subst-∙ :  { ℓ'} {A : Type }  (B : A  Type ℓ')
         {x y z : A} (p : x  y) (q : y  z) (u : B x)
         subst B (p  q) u  subst B q (subst B p u)
subst-∙ B p q Bx i =
  transport (ap B (∙-filler' p q (~ i))) (transport-filler-ext (ap B p) i Bx)

```
-->