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}