Martin Escardo.

Formulation of function extensionality. Notice that this file doesn't
postulate function extensionality. It only defines the concept, which
is used explicitly as a hypothesis each time it is needed.

\begin{code}

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

module UF.FunExt where

open import MLTT.Spartan
open import UF.Base
open import UF.Equiv
open import UF.LeftCancellable

\end{code}

The appropriate notion of function extensionality in univalent
mathematics is funext, define below. It is implied, by an argument due
to Voevodky, by naive, non-dependent function extensionality, written
naive-funext here.

\begin{code}

naive-funext :  𝓤 𝓥  𝓤   𝓥  ̇
naive-funext 𝓤 𝓥 = {X : 𝓤 ̇ } {Y : 𝓥 ̇ } {f g : X  Y}  f  g  f  g

DN-funext :  𝓤 𝓥  𝓤   𝓥  ̇
DN-funext 𝓤 𝓥 = {X : 𝓤 ̇ } {A : X  𝓥 ̇ } {f g : Π A}  f  g  f  g

funext :  𝓤 𝓥  𝓤   𝓥  ̇
funext 𝓤 𝓥 = {X : 𝓤 ̇ } {A : X  𝓥 ̇ } (f g : Π A)  is-equiv (happly' f g)

funext₀ : 𝓤₁ ̇
funext₀ = funext 𝓤₀ 𝓤₀

FunExt : 𝓤ω
FunExt = (𝓤 𝓥 : Universe)  funext 𝓤 𝓥

Fun-Ext : 𝓤ω
Fun-Ext = {𝓤 𝓥 : Universe}  funext 𝓤 𝓥

≃-funext : funext 𝓤 𝓥  {X : 𝓤 ̇ } {A : X  𝓥 ̇ } (f g : Π A)
          (f  g)  (f  g)
≃-funext fe f g = happly' f g , fe f g

abstract
 dfunext : funext 𝓤 𝓥  DN-funext 𝓤 𝓥
 dfunext fe {X} {A} {f} {g} = inverse (happly' f g) (fe f g)

 happly-funext : {X : 𝓤 ̇ } {A : X  𝓥 ̇ }
                 (fe : funext 𝓤 𝓥) (f g : Π A) (h : f  g)
                happly (dfunext fe h)  h
 happly-funext fe f g = inverses-are-sections happly (fe f g)

 funext-happly : {X : 𝓤 ̇ } {A : X  𝓥 ̇ } (fe : funext 𝓤 𝓥)
                (f g : Π A) (h : f  g)
                dfunext fe (happly h)  h
 funext-happly fe f g refl = inverses-are-retractions happly (fe f f) refl

happly-≃ : {X : 𝓤 ̇ } {A : X  𝓥 ̇ }
           (fe : funext 𝓤 𝓥)
           {f g : (x : X)  A x}
          (f  g)  f  g
happly-≃ fe = happly , fe _ _

funext-lc : {X : 𝓤 ̇ } {A : X  𝓥 ̇ }
            (fe : funext 𝓤 𝓥)
            (f g : Π A)
           left-cancellable (dfunext fe {X} {A} {f} {g})
funext-lc fe f g = section-lc (dfunext fe) (happly , happly-funext fe f g)

happly-lc : {X : 𝓤 ̇ } {A : X  𝓥 ̇ }
            (fe : funext 𝓤 𝓥)
            (f g : Π A)
           left-cancellable (happly' f g)
happly-lc fe f g = section-lc happly (equivs-are-sections happly (fe f g))

inverse-of-happly-is-dfunext : {A : 𝓤 ̇ } {B : 𝓥 ̇ }
                               (fe₀ : funext 𝓤 𝓥)
                               (fe₁ : funext (𝓤  𝓥) (𝓤  𝓥))
                               (f g : A  B)
                              inverse (happly' f g) (fe₀ f g)  dfunext fe₀
inverse-of-happly-is-dfunext fe₀ fe₁ f g =
 dfunext fe₁
   h  happly-lc fe₀ f g
          (happly' f g (inverse (happly' f g) (fe₀ f g) h)
                                       =⟨ inverses-are-sections _ (fe₀ f g) h 
           h                           =⟨ happly-funext fe₀ f g h ⁻¹ 
           happly' f g (dfunext fe₀ h) ))

dfunext-refl : {X : 𝓤 ̇ } {A : X  𝓥 ̇ }
               (fe : funext 𝓤 𝓥)
               (f : Π A)
              dfunext fe  (x : X)  𝓻𝓮𝒻𝓵 (f x))  refl
dfunext-refl fe f = happly-lc fe f f (happly-funext fe f f  x  refl))

ap-funext : {X : 𝓥 ̇ } {Y : 𝓦 ̇ }
            (f g : X  Y)
            {A : 𝓦' ̇ } (k : Y  A)
            (h : f  g)
            (fe : funext 𝓥 𝓦) (x : X)
           ap  (- : X  Y)  k (- x)) (dfunext fe h)  ap k (h x)
ap-funext f g k h fe x = ap  -  k (- x)) (dfunext fe h)    =⟨ refl 
                         ap (k   -  - x)) (dfunext fe h)  =⟨ I 
                         ap k (ap  -  - x) (dfunext fe h)) =⟨ refl 
                         ap k (happly (dfunext fe h) x)       =⟨ II 
                         ap k (h x)                           
                          where
                           I  = (ap-ap  -  - x) k (dfunext fe h))⁻¹
                           II = ap  -  ap k (- x)) (happly-funext fe f g h)

ap-precomp-funext : {X : 𝓤 ̇ } {Y : 𝓥 ̇ } {A : 𝓦 ̇ }
                    (f g : X  Y)
                    (k : A  X) (h : f  g)
                    (fe₀ : funext 𝓤 𝓥)
                    (fe₁ : funext 𝓦 𝓥)
                   ap (_∘ k) (dfunext fe₀ h)  dfunext fe₁ (h  k)
ap-precomp-funext f g k h fe₀ fe₁ =
  ap (_∘ k) (dfunext fe₀ h)                        =⟨ I 
  dfunext fe₁ (happly (ap (_∘ k) (dfunext fe₀ h))) =⟨ II 
  dfunext fe₁ (h  k)                              
   where
    I  = funext-happly fe₁ (f  k) (g  k) _ ⁻¹

    III = λ x 
     ap  -  - x) (ap (_∘ k) (dfunext fe₀ h)) =⟨ ap-ap _ _ (dfunext fe₀ h) 
     ap  -  - (k x)) (dfunext fe₀ h)         =⟨ ap-funext f g id h fe₀ (k x) 
     ap  v  v) (h (k x))                     =⟨ ap-id-is-id (h (k x)) 
     h (k x)                                    

    II = ap (dfunext fe₁) (dfunext fe₁ III)

\end{code}

The following is taken from this thread:
https://groups.google.com/forum/#!msg/homotopytypetheory/VaLJM7S4d18/Lezr_ZhJl6UJ

\begin{code}

transport-funext : {X : 𝓤 ̇ } (A : X  𝓥 ̇ )
                   (P : (x : X)  A x  𝓦 ̇ )
                   (fe : funext 𝓤 𝓥)
                   (f g : Π A)
                   (φ : (x : X)  P x (f x))
                   (h : f  g)
                   (x : X)
                  transport  -  (x : X)  P x (- x)) (dfunext fe h) φ x
                  transport (P x) (h x) (φ x)
transport-funext A P fe f g φ h x =
 transport  -   x  P x (- x)) (dfunext fe h) φ x =⟨ I 
 transport (P x) (happly (dfunext fe h) x) (φ x)      =⟨ II 
 transport (P x) (h x) (φ x) 
 where
  lemma : (f g : Π A) (φ :  x  P x (f x)) (p : f  g)
          x  transport  -   x  P x (- x)) p φ x
               transport (P x) (happly p x) (φ x)
  lemma f f φ refl x = refl

  I  = lemma f g φ (dfunext fe h) x
  II = ap  -  transport (P x) (- x) (φ x)) (happly-funext fe f g h)

transport-funext' : {X : 𝓤 ̇ } (A : 𝓥 ̇ )
                    (P : X  A  𝓦 ̇ )
                    (fe : funext 𝓤 𝓥)
                    (f g : X  A)
                    (φ : (x : X)  P x (f x))
                    (h : f  g)
                    (x : X)
                  transport  -  (x : X)  P x (- x)) (dfunext fe h) φ x
                  transport (P x) (h x) (φ x)
transport-funext' A P = transport-funext  _  A) P

\end{code}

Added 22nd October 2024. Implicit DN-funext.

\begin{code}

Πᵢ : {X : 𝓤 ̇ } (A : X  𝓥 ̇ )  𝓤  𝓥 ̇
Πᵢ {𝓤} {𝓥} {X} A = {x : X}  A x

module _ {X : 𝓤 ̇ } {A : X  𝓥 ̇ } where

 infix  4 _∼ᵢ_

 _∼ᵢ_ :  Πᵢ A  Πᵢ A  𝓤  𝓥 ̇
 f ∼ᵢ g =  x  f {x}  g {x}

 explicit : Πᵢ A  Π A
 explicit f x = f {x}

 implicit : Π A  Πᵢ A
 implicit f {x} = f x

 ∼ᵢ-gives-∼ : (f g : Πᵢ A)  f ∼ᵢ g  explicit f  explicit g
 ∼ᵢ-gives-∼ f g h x = h x

 ∼-gives-∼ᵢ : (f g : Π A)  f  g  implicit f ∼ᵢ implicit g
 ∼-gives-∼ᵢ f g h x = h x

 implicit-η-rule : (f : Πᵢ A)   {x}  f {x})  f
 implicit-η-rule f = refl

\end{code}

Agda gets confused when we try to write f = g for f g : Πᵢ A, because
it thinks that an implicit argument is implicitly applied to f and g,
but then it is not able to infer it. To prevent this from happening,
can write (λ {x} → f {x}) = g, which is ugly, but amounts to the
equality f = g.

("Implicit arguments are inserted eagerly in left-hand sides" https://agda.readthedocs.io/en/latest/language/implicit-arguments.html)

Our solution is to instead write f =[ Πᵢ A ] g. We
use a similar trick for _∼ᵢ_.

\begin{code}

-∼ᵢ : {X : 𝓤 ̇ } (A : X  𝓥 ̇ )  Πᵢ A  Πᵢ A  𝓤  𝓥 ̇
-∼ᵢ A f g =  x  f {x}  g {x}

syntax -∼ᵢ A f g = f ∼ᵢ[ A ] g

implicit-DN-funext :  𝓤 𝓥  (𝓤  𝓥) ̇
implicit-DN-funext 𝓤 𝓥 = {X : 𝓤 ̇ } {A : X  𝓥 ̇ } {f g : Πᵢ A}
                         f ∼ᵢ[ A ] g
                         f =[ Πᵢ A ] g

implicit-dfunext : funext 𝓤 𝓥  implicit-DN-funext 𝓤 𝓥
implicit-dfunext fe {X} {A} {f} {g} h = ap implicit (dfunext fe (∼ᵢ-gives-∼ f g h))

\end{code}