\begin{code}

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

module UF.Retracts-FunExt where

open import MLTT.Spartan
open import UF.Retracts
open import UF.FunExt

retract-variance : ∀ {𝓣} {X : 𝓤 ̇ } {Y : 𝓥 ̇ } {X' : 𝓦 ̇ } {Y' : 𝓣 ̇ }
                 → funext 𝓤 𝓣
                 → retract X of X'
                 → retract Y' of Y
                 → retract (X → Y') of (X' → Y)
retract-variance
 {𝓤} {𝓥} {𝓦} {𝓣} {X} {Y} {X'} {Y'} fe (rx , sx , rsx) (ry , sy , rsy) = γ
 where
  r : (X' → Y) → X → Y'
  r f x = ry (f (sx x))

  s : (X → Y') → X' → Y
  s f' x' = sy (f' (rx x'))

  rs' : (f' : X → Y') (x : X) → ry (sy (f' (rx (sx x)))) = f' x
  rs' f' x = rsy (f' (rx (sx x))) ∙ ap f' (rsx x)

  rs : (f' : X → Y') → r (s f') = f'
  rs f' = dfunext fe (rs' f')

  γ : retract (X → Y') of (X' → Y)
  γ = (r , s , rs)

retract-contravariance : funext 𝓤 𝓦
                       → {X : 𝓤 ̇ } {Y : 𝓥 ̇ } {Y' : 𝓦 ̇ }
                       → retract Y' of Y
                       → retract (X → Y') of (X → Y)
retract-contravariance fe = retract-variance fe identity-retraction

retract-covariance : {X : 𝓤 ̇ } {Y : 𝓥 ̇ } {X' : 𝓦 ̇ }
                   → funext 𝓤 𝓥
                   → retract X of X'
                   → retract (X → Y) of (X' → Y)
retract-covariance fe rx = retract-variance fe rx identity-retraction

codomain-is-retract-of-function-space-with-pointed-domain : {X : 𝓤 ̇ } {Y : 𝓥 ̇ }
                                                          → X
                                                          → retract Y of (X → Y)
codomain-is-retract-of-function-space-with-pointed-domain x =
 ((λ f → f x) , ((λ y x → y) , λ y → refl))

retracts-of-closed-under-exponentials : {X : 𝓤 ̇ } {Y : 𝓥 ̇ } {B : 𝓦 ̇ }
                                      → funext 𝓦 𝓦
                                      → X
                                      → retract B of X
                                      → retract B of Y
                                      → retract B of (X → Y)
retracts-of-closed-under-exponentials {𝓤} {𝓥} {𝓦} {X} {Y} {B} fe x rbx rby = ii
 where
  i : retract (B → B) of (X → Y)
  i = retract-variance fe rbx rby

  ii : retract B of (X → Y)
  ii = retracts-compose i (codomain-is-retract-of-function-space-with-pointed-domain (pr₁ rbx x))

\end{code}