\begin{code}

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

module UF-Yoneda where

open import SpartanMLTT
open import UF-Base
open import UF-Subsingletons
open import UF-Subsingletons-FunExt
open import UF-Retracts
open import UF-Equiv
open import UF-FunExt
open import UF-Equiv-FunExt
open import UF-EquivalenceExamples

\end{code}

We now consider "natural transformations" Nat A B (defined elsewhere)
and the Yoneda-machinery for them as discussed in
http://www.cs.bham.ac.uk/~mhe/yoneda/yoneda.html

The Yoneda element induced by a natural transformation:

\begin{code}

yoneda-elem : {X : π€ Μ } (x : X) (A : X β π₯ Μ )
β Nat (Id x) A β A x
yoneda-elem x A Ξ· = Ξ· x refl

\end{code}

We use capital Yoneda for the same constructions with the definition
of "Nat" expanded, beginning here:

\begin{code}

Yoneda-elem : {X : π€ Μ } (x : X) (A : X β π₯ Μ )
β ((y : X) β x β‘ y β A y) β A x
Yoneda-elem = yoneda-elem

\end{code}

The natural transformation induced by an element:

\begin{code}

yoneda-nat : {X : π€ Μ } (x : X) (A : X β π₯ Μ )
β A x β Nat (Id x) A
yoneda-nat x A a y p = transport A p a

Yoneda-nat : {X : π€ Μ } (x : X) (A : X β π₯ Μ )
β A x β (y : X) β x β‘ y β A y
Yoneda-nat = yoneda-nat

\end{code}

Notice that this is the based recursion principle for the identity type.

The Yoneda Lemma says that every natural transformation is induced by
its Yoneda element:

\begin{code}

yoneda-lemma : {X : π€ Μ } (x : X) (A : X β π₯ Μ ) (Ξ· : Nat (Id x) A)
β yoneda-nat x A (yoneda-elem x A Ξ·) β Ξ·
yoneda-lemma x A Ξ· y refl = refl

Yoneda-lemma : {X : π€ Μ } (x : X) (A : X β π₯ Μ )
(Ξ· : (y : X) β x β‘ y β A y) (y : X) (p : x β‘ y)
β transport A p (Ξ· x refl) β‘ Ξ· y p
Yoneda-lemma = yoneda-lemma

\end{code}

From another point of view, the Yoneda Lemma says that every natural
transformation Ξ· is recursively defined.

\begin{code}

yoneda-lemma' : FunExt
β {X : π€ Μ } (x : X) (A : X β π₯ Μ ) (Ξ· : Nat (Id x) A)
β yoneda-nat x A (yoneda-elem x A Ξ·) β‘ Ξ·
yoneda-lemma' {π€} {π₯} fe x A Ξ· = dfunext (fe π€ (π€ β π₯))
(Ξ» y β dfunext (fe π€ π₯)
(Ξ» p β yoneda-lemma x A Ξ· y p))

\end{code}

The word "computation" here arises from a tradition in MLTT and should
not be taken too seriously:

\begin{code}

Yoneda-computation : {X : π€ Μ } (x : X) (A : X β π₯ Μ ) (a : A x)
β transport A refl a β‘ a
Yoneda-computation x A a = refl

yoneda-computation : {X : π€ Μ } (x : X) (A : X β π₯ Μ ) (a : A x)
β yoneda-elem x A (yoneda-nat x A a) β‘ a
yoneda-computation x A = Yoneda-computation x A

yoneda-elem-is-equiv : FunExt
β {X : π€ Μ } (x : X) (A : X β π₯ Μ )
β is-equiv (yoneda-elem x A)
yoneda-elem-is-equiv fe x A = qinvs-are-equivs (yoneda-elem x A)
(yoneda-nat x A ,
yoneda-lemma' fe x A ,
yoneda-computation x A)

yoneda-nat-is-equiv : FunExt
β {X : π€ Μ } (x : X) (A : X β π₯ Μ )
β is-equiv (yoneda-nat x A)
yoneda-nat-is-equiv fe {X} x A = qinvs-are-equivs (yoneda-nat x A)
(yoneda-elem x A ,
yoneda-computation x A ,
yoneda-lemma' fe x A)

yoneda-equivalence : FunExt
β {X : π€ Μ } (x : X) (A : X β π₯ Μ )
β A x β Nat (Id x) A
yoneda-equivalence fe x A = yoneda-nat x A , yoneda-nat-is-equiv fe x A

Yoneda-equivalence : FunExt
β {X : π€ Μ } (x : X) (A : X β π₯ Μ )
β A x β (β y β x β‘ y β A y)
Yoneda-equivalence = yoneda-equivalence

\end{code}

Next we observe that "only elements", or centers of contraction, are
universal elements in the sense of category theory.

\begin{code}

is-universal-element-of : {X : π€ Μ } (A : X β π₯ Μ ) β Ξ£ A β π€ β π₯ Μ
is-universal-element-of {π€} {π₯} {X} A (x , a) =
(y : X) (b : A y) β Ξ£ p κ x β‘ y , yoneda-nat x A a y p β‘ b

universal-element-is-central : {X : π€ Μ } {A : X β π₯ Μ } (Ο : Ξ£ A)
β is-universal-element-of A Ο
β is-central (Ξ£ A) Ο
universal-element-is-central (x , a) u (y , b) = to-Ξ£-β‘ (u y b)

central-point-is-universal : {X : π€ Μ } (A : X β π₯ Μ ) (Ο : Ξ£ A)
β is-central (Ξ£ A) Ο
β is-universal-element-of A Ο
central-point-is-universal A (x , a) Ο y b = from-Ξ£-β‘ (Ο(y , b))

\end{code}

The following says that if the pair (x,a) is a universal element, then
the natural transformation it induces (namely yoneda-nat x a)
has a section and a retraction (which can be taken to be the same
function), and hence is an equivalence. Here having a section or
retraction is data not property in general, but it is in some cases
considered below.

\begin{code}

universality-section : {X : π€ Μ } {A : X β π₯ Μ } (x : X) (a : A x)
β is-universal-element-of A (x , a)
β (y : X) β has-section (yoneda-nat x A a y)
universality-section {π€} {π₯} {X} {A} x a u y = s y , Ο y
where
s : (y : X) β A y β x β‘ y
s y b = prβ (u y b)
Ο : (y : X) (b : A y) β yoneda-nat x A a y (s y b) β‘ b
Ο y b = prβ (u y b)

section-universality : {X : π€ Μ } {A : X β π₯ Μ } (x : X) (a : A x)
β ((y : X) β has-section(yoneda-nat x A a y))
β is-universal-element-of A (x , a)
section-universality x a Ο y b = prβ(Ο y) b , prβ(Ο y) b

\end{code}

NB. Notice that Yoneda-nat gives two different natural
transformations, depending on the number of arguments it takes, namely
the natural transformation (x : X) β A x β Nat (Id x) A and the
natural transformation Nat (Id x) β A (or (y : X) β x β‘ y β A y) if
two additional arguments x and a are given.

Then the Yoneda Theorem (proved below) says that any Ξ· : Nat (Id x) A)
is a natural equivalence iff Ξ£ A is a singleton. This, in turn, is
equivalent to Ξ· being a natural retraction, and we start with it:

\begin{code}

Yoneda-section-forth : {X : π€ Μ } {A : X β π₯ Μ } (x : X) (Ξ· : Nat (Id x) A)
β β! A β (y : X) β has-section (Ξ· y)
Yoneda-section-forth {π€} {π₯} {X} {A} x Ξ· i y = g
where
u : is-universal-element-of A (x , yoneda-elem x A Ξ·)
u = central-point-is-universal A
(x , yoneda-elem x A Ξ·)
(singletons-are-props i (x , yoneda-elem x A Ξ·))
h : yoneda-nat x A (yoneda-elem x A Ξ·) y βΌ Ξ· y
h = yoneda-lemma x A Ξ· y
g : has-section (Ξ· y)
g = has-section-closed-under-βΌ' (universality-section x (yoneda-elem x A Ξ·) u y) h

Yoneda-section-back : {X : π€ Μ } {A : X β π₯ Μ } (x : X) (Ξ· : Nat (Id x) A)
β ((y : X) β has-section (Ξ· y)) β β! A
Yoneda-section-back {π€} {π₯} {X} {A} x Ξ· Ο = c
where
h : β y β yoneda-nat x A (yoneda-elem x A Ξ·) y βΌ Ξ· y
h = yoneda-lemma x A Ξ·
g : β y β has-section (yoneda-nat x A (yoneda-elem x A Ξ·) y)
g y = has-section-closed-under-βΌ (Ξ· y) (yoneda-nat x A (yoneda-elem x A Ξ·) y) (Ο y) (h y)
u : is-universal-element-of A (x , yoneda-elem x A Ξ·)
u = section-universality x (yoneda-elem x A Ξ·) g
c : β! A
c = (x , yoneda-elem x A Ξ·) , (universal-element-is-central (x , yoneda-elem x A Ξ·) u)

Yoneda-section : {X : π€ Μ } {A : X β π₯ Μ } (x : X) (Ξ· : Nat (Id x) A)
β β! A β ((y : X) β has-section (Ξ· y))
Yoneda-section x Ξ· = Yoneda-section-forth x Ξ· , Yoneda-section-back x Ξ·

\end{code}

Here is a direct application (24th April 2018).

\begin{code}

equiv-adj : {X : π€ Μ } {Y : π₯ Μ } (f : X β Y) (g : Y β X)
(Ξ· : (x : X) (y : Y) β f x β‘ y β g y β‘ x)
β ((x : X) (y : Y) β has-section (Ξ· x y)) β is-vv-equiv g
equiv-adj f g Ξ· = (Ξ» i x β Yoneda-section-back (f x) (Ξ· x) (i x)) ,
(Ξ» Ο x β Yoneda-section-forth (f x) (Ξ· x) (Ο x))

\end{code}

This motivates the following definition.

\begin{code}

has-adj : {X : π€ Μ } {Y : π₯ Μ } β (Y β X) β π€ β π₯ Μ
has-adj g = Ξ£ f κ (codomain g β domain g)
, Ξ£ Ξ· κ (β x y β f x β‘ y β g y β‘ x)
, (β x y β has-section(Ξ· x y))

is-vv-equiv-has-adj : {X : π€ Μ } {Y : π₯ Μ } (g : Y β X)
β is-vv-equiv g β has-adj g
is-vv-equiv-has-adj {π€} {π₯} {X} {Y} g isv = f , Ξ· , hass
where
f : X β Y
f = prβ (prβ (vv-equivs-are-equivs g isv))
gf : (x : X) β g (f x) β‘ x
gf = prβ (prβ (vv-equivs-are-equivs g isv))
Ξ· : (x : X) (y : Y) β f x β‘ y β g y β‘ x
Ξ· x y p = transport (Ξ» - β g - β‘ x) p (gf x )
hass : (x : X) (y : Y) β has-section (Ξ· x y)
hass x = Yoneda-section-forth (f x) (Ξ· x) (isv x)

has-adj-is-vv-equiv : {X : π€ Μ } {Y : π₯ Μ } (g : Y β X)
β has-adj g β is-vv-equiv g
has-adj-is-vv-equiv g (f , Ξ· , hass) x = Yoneda-section-back (f x) (Ξ· x) (hass x)

\end{code}

A natural transformation of the above kind is an equivalence iff it has a section,
as shown in https://github.com/HoTT/book/issues/718#issuecomment-65378867:

\begin{code}

Hedberg-lemma : {X : π€ Μ } (x : X) (Ξ· : (y : X) β x β‘ y β x β‘ y) (y : X) (p : x β‘ y)
β Ξ· x refl β p β‘ Ξ· y p
Hedberg-lemma x Ξ· = yoneda-lemma x (Id x) Ξ·

idemp-is-id : {X : π€ Μ } {x : X} (e : (y : X) β x β‘ y β x β‘ y) (y : X) (p : x β‘ y)
β e y (e y p) β‘ e y p
β e y p β‘ p
idemp-is-id {π€} {X} {x} e y p idemp = cancel-left (
e x refl β e y p β‘β¨ Hedberg-lemma x e y (e y p) β©
e y (e y p)      β‘β¨ idemp β©
e y p            β‘β¨ (Hedberg-lemma x e y p)β»ΒΉ β©
e x refl β p     β)

nat-retraction-is-section : {X : π€ Μ } {A : X β π₯ Μ } (x : X) (Ξ· : Nat (Id x) A)
β ((y : X) β has-section(Ξ· y))
β ((y : X) β is-section(Ξ· y))
nat-retraction-is-section {π€} {π₯} {X} {A} x Ξ· hs = hr
where
s : (y : X) β A y β x β‘ y
s y = prβ (hs y)
Ξ·s : {y : X} (a : A y) β Ξ· y (s y a) β‘ a
Ξ·s {y} = prβ (hs y)
e : (y : X) β x β‘ y β x β‘ y
e y p = s y (Ξ· y p)
idemp : (y : X) (p : x β‘ y) β e y (e y p) β‘ e y p
idemp y p = ap (s y) (Ξ·s (Ξ· y p))
i : (y : X) (p : x β‘ y) β e y p β‘ p
i y p = idemp-is-id e y p (idemp y p)
hr : (y : X) β is-section(Ξ· y)
hr y = s y , i y

\end{code}

The above use of the word "is" is justified by the following:

\begin{code}

nat-retraction-is-section-uniquely : FunExt
β {X : π€ Μ } {A : X β π₯ Μ }
(x : X) (Ξ· : Nat (Id x) A)
β ((y : X) β has-section(Ξ· y))
β ((y : X) β is-singleton(is-section(Ξ· y)))
nat-retraction-is-section-uniquely fe x Ξ· hs y = pointed-props-are-singletons
(nat-retraction-is-section x Ξ· hs y)
(sections-have-at-most-one-retraction fe (Ξ· y) (hs y))

nat-having-section-is-prop : FunExt
β {X : π€ Μ } {A : X β π₯ Μ }
(x : X) (Ξ· : Nat (Id x) A)
β is-prop ((y : X) β has-section (Ξ· y))
nat-having-section-is-prop {π€} {π₯} fe {X} x Ξ· Ο = Ξ -is-prop (fe π€ (π€ β π₯)) Ξ³ Ο
where
Ξ³ : (y : X) β is-prop (has-section (Ξ· y))
Ξ³ y = retractions-have-at-most-one-section fe (Ξ· y) (nat-retraction-is-section x Ξ· Ο y)

nats-with-sections-are-equivs : {X : π€ Μ } {A : X β π₯ Μ } (x : X) (Ξ· : Nat (Id x) A)
β ((y : X) β has-section(Ξ· y))
β is-fiberwise-equiv Ξ·
nats-with-sections-are-equivs x Ξ· hs y = (hs y , nat-retraction-is-section x Ξ· hs y)

\end{code}

We are interested in the following corollaries:

\begin{code}

universality-equiv : {X : π€ Μ } {A : X β π₯ Μ } (x : X) (a : A x)
β is-universal-element-of A (x , a)
β is-fiberwise-equiv (yoneda-nat x A a)
universality-equiv {π€} {π₯} {X} {A} x a u = nats-with-sections-are-equivs x
(yoneda-nat x A a)
(universality-section x a u)

equiv-universality : {X : π€ Μ } {A : X β π₯ Μ } (x : X) (a : A x)
β is-fiberwise-equiv (yoneda-nat x A a )
β is-universal-element-of A (x , a)
equiv-universality x a Ο = section-universality x a (Ξ» y β prβ (Ο y))

Yoneda-Theorem-forth : {X : π€ Μ } {A : X β π₯ Μ } (x : X) (Ξ· : Nat (Id x) A)
β β! A β is-fiberwise-equiv Ξ·
Yoneda-Theorem-forth x Ξ· i = nats-with-sections-are-equivs x Ξ· (Yoneda-section-forth x Ξ· i)

\end{code}

Here is another proof, from the MGS'2019 lecture notes (https://github.com/martinescardo/HoTT-UF-Agda-Lecture-Notes):

\begin{code}

Yoneda-Theorem-forth' : {X : π€ Μ } (A : X β π₯ Μ ) (x : X) (Ξ· : Nat (Id x) A)
β β! A β is-fiberwise-equiv Ξ·
Yoneda-Theorem-forth' {π€} {π₯} {X} A x Ξ· u = Ξ³
where
g : singleton-type x β Ξ£ A
g = NatΞ£ Ξ·

e : is-equiv g
e = maps-of-singletons-are-equivs g (singleton-types-are-singletons x) u

Ξ³ : is-fiberwise-equiv Ξ·
Ξ³ = NatΞ£-equiv-gives-fiberwise-equiv Ξ· e

\end{code}

Here is an application:

\begin{code}

fiberwise-equiv-criterion : {X : π€ Μ } (A : X β π₯ Μ )
(x : X)
β ((y : X) β A y β (x β‘ y))
β (Ο : Nat (Id x) A) β is-fiberwise-equiv Ο
fiberwise-equiv-criterion A x Ο Ο = Yoneda-Theorem-forth x Ο
(Yoneda-section-back x
(Ξ» x β retraction (Ο x))
(Ξ» x β retraction-has-section (Ο x)))

fiberwise-equiv-criterion' : {X : π€ Μ } (A : X β π₯ Μ )
(x : X)
β ((y : X) β (x β‘ y) β A y)
β (Ο : Nat (Id x) A) β is-fiberwise-equiv Ο
fiberwise-equiv-criterion' A x e = fiberwise-equiv-criterion A x
(Ξ» y β β-gives-β· (e y))

\end{code}

This says that is there is any fiberwise equivalence whatsoever (or
even just a fiberwise retraction), then any natural transformation is
a fiberwise equivalence.

\begin{code}

Yoneda-Theorem-back : {X : π€ Μ } {A : X β π₯ Μ } (x : X) (Ξ· : Nat (Id x) A)
β is-fiberwise-equiv Ξ· β β! A
Yoneda-Theorem-back x Ξ· Ο = Yoneda-section-back x Ξ· (Ξ» y β prβ(Ο y))

\end{code}

Next we conclude that a presheaf A is representable iff Ξ£ A is a
singleton.

\begin{code}

_β_ : {X : π€ Μ } β (X β π₯ Μ ) β (X β π¦ Μ ) β π€ β π₯ β π¦ Μ
A β B = Ξ£ Ξ· κ Nat A B , is-fiberwise-equiv Ξ·

is-representable : {X : π€ Μ } β (X β π₯ Μ ) β π€ β π₯ Μ
is-representable A = Ξ£ x κ domain A , Id x β A

singleton-representable : {X : π€ Μ } {A : X β π₯ Μ }
β β! A β is-representable A
singleton-representable {π€} {π₯} {X} {A} ((x , a) , cc) =
x , yoneda-nat x A a , Yoneda-Theorem-forth x (yoneda-nat x A a) ((x , a) , cc)

representable-singleton : {X : π€ Μ } {A : X β π₯ Μ }
β is-representable A β β! A
representable-singleton (x , (Ξ· , Ο)) = Yoneda-Theorem-back x Ξ· Ο

\end{code}

We also have the following corollaries:

\begin{code}

is-vv-equiv-has-adj' : {X : π€ Μ } {Y : π₯ Μ } (g : Y β X)
β is-vv-equiv g
β Ξ£ f κ (X β Y) , ((x : X) (y : Y) β (f x β‘ y) β (g y β‘ x))
is-vv-equiv-has-adj' g Ο = prβ Ξ³ ,
Ξ» x y β prβ (prβ Ξ³) x y ,
nats-with-sections-are-equivs
(prβ Ξ³ x) (prβ (prβ Ξ³) x) (prβ (prβ Ξ³) x) y
where

has-adj-is-vv-equiv' : {X : π€ Μ } {Y : π₯ Μ } (g : Y β X)
β (Ξ£ f κ (X β Y) , ((x : X) (y : Y) β (f x β‘ y) β (g y β‘ x)))
β is-vv-equiv g
has-adj-is-vv-equiv' g (f , Ο) =
has-adj-is-vv-equiv g (f , (Ξ» x y β prβ(Ο x y)) , (Ξ» x y β prβ(prβ(Ο x y))))

\end{code}

Here is an application of the Yoneda machinery to a well-known result
by Voevodsky. If products preserve contractibility, then function
extensionality holds (happly is an equivalence).

\begin{code}

funext-via-singletons :
((X : π€ Μ ) (Y : X β π₯ Μ )
β ((x : X) β is-singleton (Y x)) β is-singleton (Ξ  Y))
β funext π€ π₯
funext-via-singletons {π€} {π₯} Ο {X} {Y} f = Ξ³
where
c : is-singleton (Ξ  x κ X , Ξ£ y κ Y x , f x β‘ y)
c = Ο X (Ξ» x β Ξ£ y κ Y x , f x β‘ y) (Ξ» x β singleton-types-are-singletons (f x))
A : Ξ  Y β π€ β π₯ Μ
A g = (x : X) β f x β‘ g x
r : (Ξ  x κ X , Ξ£ y κ Y x , f x β‘ y) β Ξ£ A
r = TT-choice
r-has-section : has-section r
r-has-section = TT-choice-has-section
d : β! A
d = retract-of-singleton (r , r-has-section) c
Ξ· : Nat (Id f) A
Ξ· = happly' f
Ξ³ : (g : Ξ  Y) β is-equiv (happly' f g)
Ξ³ = Yoneda-Theorem-forth f Ξ· d

\end{code}

We also have the following characterization of univalence from the
Yoneda machinery.

The fact that this is the case was announced on 5th August
2014 with the techniques of the HoTT Book
and the proof given here via Yoneda was announced on 12th May 2015
(http://www.cs.bham.ac.uk/~mhe/yoneda/yoneda.html).

\begin{code}

open import UF-Univalence

univalence-via-singletonsβ : is-univalent π€ β (X : π€ Μ ) β β! Y κ π€ Μ  , X β Y
univalence-via-singletonsβ ua X = representable-singleton (X , (idtoeq X , ua X))

univalence-via-singletonsβ : ((X : π€ Μ ) β β! Y κ π€ Μ  , X β Y) β is-univalent π€
univalence-via-singletonsβ Ο X = universality-equiv X (β-refl X)
(central-point-is-universal
(Eq X)
(X , β-refl X)
(singletons-are-props (Ο X) (X , β-refl X)))

univalence-via-singletons : is-univalent π€ β ((X : π€ Μ ) β β! Y κ π€ Μ  , X β Y)
univalence-via-singletons = (univalence-via-singletonsβ , univalence-via-singletonsβ)

\end{code}

Notice that is-singleton can be replaced by is-prop in the formulation
of this logical equivalence (exercise).

Appendix.

Two natural transformations with the same Yoneda elements are
(point-point-wise) equal. This can be proved using J (or equivalently
pattern matching), but we use the opportunity to illustrate how to use
the Yoneda Lemma.

\begin{code}

yoneda-elem-lc : {X : π€ Μ } {x : X} {A : X β π₯ Μ }
(Ξ· ΞΈ : Nat (Id x) A)
β yoneda-elem x A Ξ· β‘ yoneda-elem x A ΞΈ β Ξ· β ΞΈ
yoneda-elem-lc {π€} {π₯} {X} {x} {A} Ξ· ΞΈ q y p =
Ξ· y p                                β‘β¨ (yoneda-lemma x A Ξ· y p)β»ΒΉ β©
yoneda-nat x A (yoneda-elem x A Ξ·) y p β‘β¨ ap (Ξ» - β yoneda-nat x A - y p) q β©
yoneda-nat x A (yoneda-elem x A ΞΈ) y p β‘β¨ yoneda-lemma x A ΞΈ y p β©
ΞΈ y p β

Yoneda-elem-lc : {X : π€ Μ } {x : X} {A : X β π₯ Μ } (Ξ· ΞΈ : (y : X) β x β‘ y β A y)
β Ξ· x refl β‘ ΞΈ x refl β (y : X) (p : x β‘ y) β Ξ· y p β‘ ΞΈ y p
Yoneda-elem-lc = yoneda-elem-lc

\end{code}

Some special cases of interest, which probably speak for themselves:

\begin{code}

yoneda-nat-Id : {X : π€ Μ } (x {y} : X) β Id x y β Nat (Id y) (Id x)
yoneda-nat-Id x {y} = yoneda-nat y (Id x)

Yoneda-nat-Id : {X : π€ Μ } (x {y} : X) β x β‘ y β (z : X) β y β‘ z β x β‘ z
Yoneda-nat-Id = yoneda-nat-Id

Id-charac : FunExt
β {X : π€ Μ } (x {y} : X) β (x β‘ y) β Nat (Id y) (Id x)
Id-charac fe {X} x {y} = yoneda-equivalence fe y (Id x)

yoneda-nat-Eq : (X {Y} : π€ Μ ) β Eq X Y β Nat (Id Y) (Eq X)
yoneda-nat-Eq X {Y} = yoneda-nat Y (Eq X)

yoneda-elem-Id : {X : π€ Μ } (x {y} : X) β Nat (Id y) (Id x) β Id x y
yoneda-elem-Id x {y} = yoneda-elem y (Id x)

Yoneda-elem-Id : {X : π€ Μ } (x {y} : X) β ((z : X) β y β‘ z β x β‘ z) β x β‘ y
Yoneda-elem-Id = yoneda-elem-Id

yoneda-lemma-Id : {X : π€ Μ } (x {y} : X) (Ξ· : Nat (Id y) (Id x)) (z : X) (p : y β‘ z)
β (yoneda-elem-Id x Ξ·) β p β‘ Ξ· z p
yoneda-lemma-Id x {y} = yoneda-lemma y (Id x)

Yoneda-lemma-Id : {X : π€ Μ } (x {y} : X) (Ξ· : (z : X) β y β‘ z β x β‘ z) (z : X) (p : y β‘ z)
β Ξ· y refl β p β‘ Ξ· z p
Yoneda-lemma-Id = yoneda-lemma-Id

yoneda-const : {X : π€ Μ } {B : π₯ Μ } {x : X} (Ξ· : Nat (Id x) (Ξ» _ β B)) (y : X) (p : x β‘ y)
β yoneda-elem x (Ξ» _ β B) Ξ· β‘ Ξ· y p
yoneda-const Ξ· = yoneda-elem-lc (Ξ» y p β yoneda-elem _ _ Ξ·) Ξ· refl

Yoneda-const : {X : π€ Μ } {B : π₯ Μ } {x : X} (Ξ· : (y : X) β x β‘ y β B) (y : X) (p : x β‘ y)
β Ξ· x refl β‘ Ξ· y p
Yoneda-const = yoneda-const

\end{code}

The following is traditionally proved by induction on the identity
type (as articulated by Jbased or J in the module SpartanMLTT), but
here we use the Yoneda machinery instead, again for the sake of
illustration.

\begin{code}

singleton-types-are-singletons-bis : {X : π€ Μ } (x : X)
β is-central (singleton-type x) (x , refl)
singleton-types-are-singletons-bis {π€} {X} x (y , p) = yoneda-const Ξ· y p
where
Ξ· : (y : X) β x β‘ y β singleton-type x
Ξ· y p = (y , p)

\end{code}

What the following says is that the Yoneda machinery could have been
taken as primitive, as an alternative to Jbased or J, in the sense
that the latter can be recovered from the former.

\begin{code}

private

Jbased'' : {X : π€ Μ } (x : X) (A : singleton-type x β π₯ Μ )
β A (x , refl) β Ξ  A
Jbased'' x A a w = yoneda-nat (x , refl) A a w (singleton-types-are-singletons' w)

Jbased' : {X : π€ Μ } (x : X) (B : (y : X) β x β‘ y β π₯ Μ )
β B x refl β (y : X) β Ξ  (B y)
Jbased' x B b y p = Jbased'' x (uncurry B) b (y , p)

\end{code}

And now some more uses of Yoneda to prove things that traditionally
are proved using J(based), again for the sake of illustration:

\begin{code}

refl-left-neutral-bis : {X : π€ Μ } {x y : X} {p : x β‘ y} β refl β p β‘ p
refl-left-neutral-bis {π€} {X} {x} {y} {p} = yoneda-lemma x (Id x) (Ξ» y p β p) y p

β»ΒΉ-involutive-bis : {X : π€ Μ } {x y : X} (p : x β‘ y) β (p β»ΒΉ)β»ΒΉ β‘ p
β»ΒΉ-involutive-bis {π€} {X} {x} {y} = yoneda-elem-lc (Ξ» x p β (p β»ΒΉ)β»ΒΉ) (Ξ» x p β p) refl y

β»ΒΉ-contravariant-bis : {X : π€ Μ } {x y : X} (p : x β‘ y) {z : X} (q : y β‘ z)
β q β»ΒΉ β p β»ΒΉ β‘ (p β q)β»ΒΉ
β»ΒΉ-contravariant-bis {π€} {X} {x} {y} p {z} = yoneda-elem-lc (Ξ» z q β q β»ΒΉ β p β»ΒΉ)
(Ξ» z q β (p β q) β»ΒΉ)
refl-left-neutral-bis
z
\end{code}

Associativity also follows from the Yoneda Lemma, again with the same
proof method. Notice that we prove that two functions (in a context)
are equal without using function extensionality:

\begin{code}

ext-assoc : {X : π€ Μ } {z t : X} (r : z β‘ t)
β (Ξ» (x y : X) (p : x β‘ y) (q : y β‘ z) β (p β q) β r)
β‘ (Ξ» (x y : X) (p : x β‘ y) (q : y β‘ z) β p β (q β r))
ext-assoc {π€} {X} {z} {t} = yoneda-elem-lc {π€} {π€} {X} {z} {Ξ» - β (x y : X) (p : x β‘ y) (q : y β‘ z) β x β‘ - }
(Ξ» z r x y p q β p β q β r)
(Ξ» z r x y p q β p β (q β r))
refl
t
\end{code}

Then of course associativity of path composition follows:

\begin{code}

assoc-bis : {X : π€ Μ } {x y z t : X} (p : x β‘ y) (q : y β‘ z) (r : z β‘ t)
β (p β q) β r β‘ p β (q β r)
assoc-bis {π€} {X} {x} {y} p q r = ap (Ξ» - β - x y p q) (ext-assoc r)

left-inverse-bis : {X : π€ Μ } {x y : X} (p : x β‘ y) β p β»ΒΉ β p β‘ refl
left-inverse-bis {π€} {X} {x} {y} = yoneda-elem-lc (Ξ» x p β p β»ΒΉ β p) (Ξ» x p β refl) refl y

right-inverse-bis : {X : π€ Μ } {x y : X} (p : x β‘ y) β refl β‘ p β p β»ΒΉ
right-inverse-bis {π€} {X} {x} {y} = yoneda-const (Ξ» x p β p β p β»ΒΉ) y

from-Ξ£-Id : {X : π€ Μ } {A : X β π₯ Μ } {Ο Ο : Ξ£ A}
β Ο β‘ Ο
β Ξ£ p κ prβ Ο β‘ prβ Ο , yoneda-nat (Ο .prβ) A (prβ Ο) (prβ Ο) p β‘ prβ Ο
from-Ξ£-Id {π€} {π₯} {X} {A} {x , a} {Ο} = yoneda-nat (x , yoneda-nat x A a x refl) B (refl , refl) Ο
where
B : (Ο : Ξ£ A) β π€ β π₯ Μ
B Ο = Ξ£ p κ x β‘ prβ Ο , yoneda-nat x A a (prβ Ο) p β‘ prβ Ο

to-Ξ£-Id : {X : π€ Μ } {A : X β π₯ Μ } {Ο Ο : Ξ£ A}
β (Ξ£ p κ prβ Ο β‘ prβ Ο , yoneda-nat (prβ Ο) A (prβ Ο) (prβ Ο) p β‘ prβ Ο)
β Ο β‘ Ο
to-Ξ£-Id {π€} {π₯} {X} {A} {x , a} {y , b} (p , q) = r
where
Ξ· : (y : X) β x β‘ y β Ξ£ A
Ξ· y p = (y , yoneda-nat x A a y p)
yc : (x , a) β‘ (y , yoneda-nat x A a y p)
yc = yoneda-const Ξ· y p
r : (x , a) β‘ (y , b)
r = yoneda-nat (yoneda-nat x A a y p) (Ξ» b β (x , a) β‘ (y , b)) yc b q

from-Ξ£-Id' : {X : π€ Μ } {A : X β π₯ Μ } {Ο Ο : Ξ£ A}
β Ο β‘ Ο
β Ξ£ p κ prβ Ο β‘ prβ Ο , transport A p (prβ Ο) β‘ prβ Ο
from-Ξ£-Id' = from-Ξ£-Id

to-Ξ£-Id' : {X : π€ Μ } {A : X β π₯ Μ } {Ο Ο : Ξ£ A}
β (Ξ£ p κ prβ Ο β‘ prβ Ο , transport A p (prβ Ο) β‘ prβ Ο)
β Ο β‘ Ο
to-Ξ£-Id' = to-Ξ£-Id

NatΞ£-lc' : {X : π€ Μ } {A : X β π₯ Μ } {B : X β π¦ Μ } (ΞΆ : Nat A B)
β ((x : X) β left-cancellable(ΞΆ x)) β left-cancellable(NatΞ£ ΞΆ)
NatΞ£-lc' {π€} {π₯} {π¦} {X} {A} {B} ΞΆ ΞΆ-lc {(x , a)} {(y , b)} pq = g
where
p : x β‘ y
p = prβ (from-Ξ£-Id pq)
Ξ· : Nat (Id x) B
Ξ· = yoneda-nat x B (ΞΆ x a)
q : Ξ· y p β‘ ΞΆ y b
q = prβ (from-Ξ£-Id pq)
ΞΈ : Nat (Id x) A
ΞΈ = yoneda-nat x A a
Ξ·' : Nat (Id x) B
Ξ·' y p = ΞΆ y (ΞΈ y p)
r : Ξ·' β Ξ·
r = yoneda-elem-lc Ξ·' Ξ· refl
r' : ΞΆ y (ΞΈ y p) β‘ Ξ· y p
r' = r y p
s : ΞΆ y (ΞΈ y p) β‘ ΞΆ y b
s = r' β q
t : ΞΈ y p β‘ b
t = ΞΆ-lc y s
g : x , a β‘ y , b
g = to-Ξ£-Id (p , t)

yoneda-equivalence-Ξ£ : FunExt
β {X : π€ Μ } (A : X β π₯ Μ )
β Ξ£ A β (Ξ£ x κ X , Nat (Id x) A)
yoneda-equivalence-Ξ£ fe A = Ξ£-cong (Ξ» x β yoneda-equivalence fe x A)

nats-are-uniquely-transports : FunExt
β {X : π€ Μ } (x : X) (A : X β π₯ Μ ) (Ξ· : Nat (Id x) A)
β β! a κ A x , (Ξ» y p β transport A p a) β‘ Ξ·
nats-are-uniquely-transports fe x A = equivs-are-vv-equivs (yoneda-nat x A) (yoneda-nat-is-equiv fe x A)

β {X : π€ Μ } {Y : π₯ Μ } (f : X β Y) (g : Y β X) (x : X)
(Ξ· : (y : Y) β f x β‘ y β g y β‘ x)
β β! q κ g (f x) β‘ x , (Ξ» y p β transport (Ξ» - β g - β‘ x) p q) β‘ Ξ·
adj-obs fe f g x = nats-are-uniquely-transports fe (f x) (Ξ» y β g y β‘ x)

\end{code}

We need this elsewhere:

\begin{code}

idtoeq-bis : (X : π€ Μ ) β Nat (Id X) (Eq X)
idtoeq-bis X = yoneda-nat X (Eq X) (β-refl X)

Idtofun' : (X : π€ Μ ) β Nat (Id X) (Ξ» Y β X β Y)
Idtofun' X = yoneda-nat X (Ξ» Y β X β Y) id

idtofun-agree' : (X : π€ Μ ) β idtofun X β Idtofun' X
idtofun-agree' X = yoneda-elem-lc (idtofun X) (Idtofun' X) refl

\end{code}