---
layout: default
title : "Legacy.Base.Equality.Welldefined module (The Agda Universal Algebra Library)"
date : "2021-07-25"
author: "agda-algebras development team"
---

### <a id="notions-of-well-definedness">Notions of well-definedness</a>


```agda


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

module Legacy.Base.Equality.Welldefined where

-- Imports from Agda and the Agda Standard Library  -------------------------------------
open import Agda.Primitive  using () renaming ( Set to Type ; Setω to Typeω )
open import Data.Fin        using ( Fin ; toℕ )
open import Data.Product    using ( _,_ ; _×_ )
open import Data.List       using ( List ; [] ; [_] ; _∷_ ; _++_ )
open import Function        using ( _$_ ; _∘_ ; id )
open import Level           using ( _⊔_ ; suc ; Level )

open import Axiom.Extensionality.Propositional     using () renaming ( Extensionality to funext )
open import Relation.Binary.PropositionalEquality  using ( _≡_ ; refl ; module ≡-Reasoning ; cong )

-- Imports from agda-algebras -----------------------------------------------------------
open import Overture        using ( _≈_ ; _⁻¹ ; Op )
open import Legacy.Base.Functions  using ( A×A→B-to-Fin2A→B ; UncurryFin2 ; UncurryFin3 )

private variable  ι α β 𝓥 ρ : Level
```


#### <a id="strongly-well-defined-operations">Strongly well-defined operations</a>

We now describe an extensionality principle that seems weaker than function
extensionality, but still (probably) not provable in [MLTT][]. (We address this
and other questions  below.)  We call this the principle *strong well-definedness
of operations*. We will encounter situations in which this weaker extensionality
principle suffices as a substitute for function extensionality.

Suppose we have a function whose domain is a function type, say, `I → A`.  For
example, inhabitants of the type `Op` defined above are such functions.  (Recall,
the domain of inhabitants of type `Op I A := (I → A) → A` is `I → A`.)

Of course, operations of type `Op I A` are well-defined in the sense that equal
inputs result in equal outputs.


```agda


welldef : {A : Type α}{I : Type 𝓥}(f : Op I A) → ∀ u v → u ≡ v → f u ≡ f v
welldef f u v = cong f
```


A stronger form of well-definedness of operations is to suppose that point-wise
equal inputs lead to the same output.  In other terms, we could suppose that  for
all `f : Op I A`, we have `f u ≡ f v` whenever `∀ i → u i ≡ v i` holds.  We call
this formalize this notation in the following type.


```agda


swelldef : ∀ ι α → Type (suc (α ⊔ ι))
swelldef ι α =  ∀ {I : Type ι}{A : Type α}(f : Op I A)(u v : I → A)
 →              u ≈ v → f u ≡ f v

funext→swelldef : {α 𝓥 : Level} → funext 𝓥 α → swelldef 𝓥 α
funext→swelldef fe f u v ptweq = welldef f u v (fe ptweq)

-- level-polymorphic version
SwellDef : Typeω
SwellDef = (α β : Level) → swelldef α β
```


There are certain situations in which a (seemingly) weaker principle than function
extensionality suffices.

Here are the more general versions of the foregoing that are not restricted to
(I-ary) *operations* on A (of type (I → A) → A), but handle also (I-ary)
*functions* from A^I to B (of type (I → A) → B).


```agda


swelldef' : ∀ ι α β → Type (suc (ι ⊔ α ⊔ β))
swelldef' ι α β =  ∀ {I : Type ι} {A : Type α} {B : Type β}
 →                 (f : (I → A) → B) {u v : I → A} → u ≈ v → f u ≡ f v

funext' : ∀ α β → Type (suc (α ⊔ β))
funext' α β = ∀ {A : Type α } {B : Type β } {f g : A → B} → f ≈ g → f ≡ g

-- `funext ι α` implies `swelldef ι α β`        (Note the universe levels!)
funext'→swelldef' : funext' ι α → swelldef' ι α β
funext'→swelldef' fe f ptweq = cong f (fe ptweq)

 -- `swelldef ι α (ι ⊔ α)` implies `funext ι α`   (Note the universe levels!)
swelldef'→funext' : swelldef' ι α (ι ⊔ α) → funext' ι α
swelldef'→funext' wd ptweq = wd _$_ ptweq
```


#### <a id="questions">Questions</a>

1.  Does the converse `swelldef→funext` hold or is `swelldef` is strictly weaker
    than `funext`?
2.  If `swelldef` is strictly weaker than `funext`, then can we prove it in MLTT?
3.  If the answer to 2 is no in general, then for what types `I` can we prove
    `swelldef 𝓥 _ {I}`?

Notice that the implication swelldef → funext holds *if* we restrict the universe
level β to be `ι ⊔ α`. This is because to go from swelldef to funext, we must
apply the swelldef premise to the special case in which `f` is the identify
function on `I → A`, which of course has type `(I → A) → (I → A)`.

This is possible if we take `swelldef ι α (ι ⊔ α)` as the premise (so that we can
assume `B` is `I → A`).

It is NOT possible if we merely assume `swelldef ι α β` for *some* β (not
necessarily `ι ⊔ α`) and for some B (not necessarily `I → A`).

In the agda-algebras library, swelldef is used exclusively on operation type, so
that B ≡ A. I believe there is no way to prove that `swelldef ι α α` implies funext ι α.


#### <a id="some-new-ideas">Some new ideas</a>

It seems unlikely that we could prove swelldef in MLTT because, on the face of it,
to prove f u ≡ f v, we need u ≡ v, but we only have ∀ i → u i ≡ v i.

    swelldef-proof : ∀ {I : Type ι}{A : Type α}{B : Type β}
     →                 (f : (I → A) → B){u v : I → A}
     →                 (∀ i → u i ≡ v i) → f u ≡ f v
    swelldef-proof {I = I}{A}{B} f {u}{v} x = {!!}  --   <== we are stuck

However, we *can* prove swelldef in MLTT for certain types at least, using a
zipper argument.

This certainly works in the special case of *finitary* functions, say,
`f : (Fin n → A) → B` for some `n`.

I expect this proof will generalize to countable arities, but I have yet to
formally prove it.

If `f` is finitary, then we can Curry and work instead with the function

    (Curry f) : A → A → A → ... → A → B

(for some appropriate number of arrow; i.e., number of arguments).

The idea is to partially apply f, and inductively build up a proof of f u ≡ f v, like so.

1.     `f (u 0)       ≡ f (v 0)`                  (by `u 0 ≡ v 0`),
2.     `f (u 0)(u 1)  ≡ f (v 0)(v 1)`             (by 1. and u 1 ≡ v 1),
⋮
n.     `f (u 0) … (u(n-1)) ≡ f (v 0) … (v(n-1))`  (by n-1 and `u(n-1) ≡ v(n-1)`).
⋮

Actually, the proof should probably go in the other direction,

⋮
n.     `f (u 0) … (u(n-2))(u(n-1)) ≡ f (u 0) … (u(n-2))(v(n-1))`
n-1.   `f (u 0)   (u(n-2))(u(n-1)) ≡ f (v 0) … (v(n-2))(v(n-1))`
⋮
2.     `f (u 0)(u 1)  ≡ f (v 0)(v 1)`
1.     `f (u 0)       ≡ f (v 0)`


To formalize this, let's begin with the simplest case, that is, when `f : A → A
→ B`, so `f` is essentially of type `(Fin 2 → A) → B`.

However, we still need to establish a one-to-one correspondence between the types
`(Fin 2 → A) → B` and `A → A → B`, (and `A × A → B`), which turns out to be nontrivial.


```agda


module _ {A : Type α}{B : Type β} where
 open Fin renaming ( zero to z ; suc to s )
 open ≡-Reasoning

 A×A-wd :  (f : A × A → B)(u v : Fin 2 → A)
  →        u ≈ v → (A×A→B-to-Fin2A→B f) u ≡ (A×A→B-to-Fin2A→B f) v

 A×A-wd f u v u≈v = Goal
  where
  zip1 : ∀ {a x y} → x ≡ y → f (a , x) ≡ f (a , y)
  zip1 refl = refl

  zip2 : ∀ {x y b} → x ≡ y → f (x , b) ≡ f (y , b)
  zip2 refl = refl

  Goal : (A×A→B-to-Fin2A→B f) u ≡ (A×A→B-to-Fin2A→B f) v
  Goal =  (A×A→B-to-Fin2A→B f) u  ≡⟨ refl ⟩
          f (u z , u (s z))       ≡⟨ zip1 (u≈v (s z)) ⟩
          f (u z , v (s z))       ≡⟨ zip2 (u≈v z) ⟩
          f (v z , v (s z))       ≡⟨ refl ⟩
          (A×A→B-to-Fin2A→B f) v  ∎

 Fin2-wd :  (f : A → A → B)(u v : Fin 2 → A)
  →         u ≈ v → (UncurryFin2 f) u ≡ (UncurryFin2 f) v

 Fin2-wd f u v u≈v = Goal
  where
  zip1 : ∀ {a x y} → x ≡ y → f a x ≡ f a y
  zip1 refl = refl

  zip2 : ∀ {x y b} → x ≡ y → f x b ≡ f y b
  zip2 refl = refl

  Goal : (UncurryFin2 f) u ≡ (UncurryFin2 f) v
  Goal = (UncurryFin2 f) u  ≡⟨ refl ⟩
         f (u z) (u (s z))  ≡⟨ zip1 (u≈v (s z)) ⟩
         f (u z) (v (s z))  ≡⟨ zip2 (u≈v z) ⟩
         f (v z) (v (s z))  ≡⟨ refl ⟩
         (UncurryFin2 f) v  ∎


 Fin3-wd :  (f : A → A → A → B)(u v : Fin 3 → A)
  →         u ≈ v → (UncurryFin3 f) u ≡ (UncurryFin3 f) v

 Fin3-wd f u v u≈v = Goal
  where
  zip1 : ∀ {a b x y} → x ≡ y → f a b x ≡ f a b y
  zip1 refl = refl

  zip2 : ∀ {a b x y} → x ≡ y → f a x b ≡ f a y b
  zip2 refl = refl

  zip3 : ∀ {a b x y} → x ≡ y → f x a b ≡ f y a b
  zip3 refl = refl

  Goal : (UncurryFin3 f) u ≡ (UncurryFin3 f) v
  Goal = (UncurryFin3 f) u                ≡⟨ refl ⟩
         f (u z) (u (s z)) (u (s (s z)))  ≡⟨ zip1 (u≈v (s (s z))) ⟩
         f (u z) (u (s z)) (v (s (s z)))  ≡⟨ zip2 (u≈v (s z)) ⟩
         f (u z) (v (s z)) (v (s (s z)))  ≡⟨ zip3 (u≈v z) ⟩
         f (v z) (v (s z)) (v (s (s z)))  ≡⟨ refl ⟩
         (UncurryFin3 f) v                ∎

 -- NEXT: try to prove (f : (Fin 2 → A) → B)(u v : Fin 2 → A) →  u ≈ v → f u ≡ f v

module _ {A : Type α}{B : Type β} where

 ListA→B :  (f : List A → B)(u v : List A) → u ≡ v → f u ≡ f v
 ListA→B f u .u refl = refl

 CurryListA : (List A → B) → (List A → A → B)
 CurryListA f [] a = f [ a ]
 CurryListA f (x ∷ l) a = f ((x ∷ l) ++ [ a ]) 

 CurryListA' : (List A → B) → (A → List A → B)
 CurryListA' f a [] = f [ a ]
 CurryListA' f a (x ∷ l) = f ([ a ] ++ (x ∷ l))
```