Exploring equality via homotopy and proof assistants - Day 3 - Inductive types and their equalities
This file contains the exercises for Day 3. Some are explicitly marked as "Homework"; the rest can be done either in class or for homework.
Compatibility between Coq 8.5 and 8.4
Set Asymmetric Patterns.
Guiding Puzzles
Puzzle 1: contractibility of based path spaces
Notation "{ x | P }" := ({ x : _ & P }) : type_scope.
Notation "{ x : A | P }" := ({ x : A & P }) : type_scope.
Notation "( x ; p )" := (existT _ x p).
A type is called contractible if there is a (continuous!) function showing that all inhabitants are equal to a particular one.
Puzzle: The following theorem is provable. Prove it.
Note that x = y :> T means x : T, y : T, and x = y. It is the notation for @eq T x y. It is how we write x =T y in Coq.
Since equals are inter-substitutable, we should be able to prove:
Definition contractible_to_UIP
: ∀ (A : Type) (y : A)
(x : A)
(p p' : y = x),
((x; p) = (x; p') :> { x : A | y = x })
→ p = p'
:= admit.
But it turns out that we can't prove this. Why not? Give both a formal, type theoretic reason (what terms don't typecheck?), and a topological reason.
Here is a concrete puzzle for the second question:
To classify the equality of a given type, we give a "code" for each pair of elements. We must say how to "encode" equality proofs, and how to "decode" codes into equality proofs. We want this to be an isomorphism, i.e., encode ∘ decode and decode ∘ encode should both be the identity function. It turns out that we don't need to know anything about encode or decode other than their existance to ensure this; we can always adjust decode to create an inverse to encode, as long as a particular property holds of code. What is that property?
More concretely, the puzzle is to fill in the following holes.
Puzzle 2: understanding the encode-decode method
Section general_classification.
Definition code_correct_P
: ∀ {A : Type} (code : A → A → Type), Type
:= admit.
We assume we are given a type, a code, an encode, and a decode. We introduce the assumptions as we get to definitions that should need them.
Context {A : Type} (code : A → A → Type)
(decode' : ∀ x y, code x y → x = y).
Definition decode
: ∀ {x y}, code x y → x = y
:= admit.
Context (encode : ∀ x y, x = y → code x y).
Definition deencode
: ∀ {x y} (p : x = y),
decode (encode _ _ p) = p
:= admit.
For this last one, we'll need the correctness property on codes.
Context (code_correct : code_correct_P code).
Definition endecode
: ∀ {x y} (p : code x y),
encode _ _ (decode p) = p
:= admit.
End general_classification.
The following sanity check on the classification should be provable:
If you didn't choose a silly correctness property like code = (fun x y ⇒ x = y), then your proof should generalize to the following:
Suppose we are given a valid encoding.
Context {A : Type} (code : A → A → Type)
(encode : ∀ x y, x = y → code x y)
(decode : ∀ x y, code x y → x = y)
(endecode : ∀ x y (p : code x y), encode _ _ (decode _ _ p) = p)
(deencode : ∀ x y (p : x = y), decode _ _ (encode _ _ p) = p).
Then it should satisfy your validity principle.
Definition Type_code
: ∀ (x y : Type), Type
:= admit.
Definition Type_encode
: ∀ {x y : Type}, x = y → Type_code x y
:= admit.
The following are unprovable in Coq, currently. They are collectively known as the "univalence axiom".
Axiom Type_decode
: ∀ {x y : Type}, Type_code x y → x = y.
Axiom Type_endecode
: ∀ {x y : Type} (p : Type_code x y),
Type_encode (Type_decode p) = p.
Axiom Type_deencode
: ∀ {x y : Type} (p : x = y),
Type_decode (Type_encode p) = p.
This was all very abstract. Let's drill down with some exmples.
First, we must understand the types which we are classifying the path-spaces of.
An inductive type is specified by introduction and elimination rules; introduction rules tell you how to create inhabitants (elements) of a type, and elimination rules tell you how to use such inhabitants. Coq generate eliminations rules automatically, but we will try to come up with the first few without peeking.
Inductive Types
Coq has special syntax for nat, so we don't overwrite it.
Inductive nat' : Type := ...
Inductive Empty_set : Type := ...
Anecdote: Proving unit → Empty_set (i.e., True → False) by recursion on unit, if we assume that True = (False → False)
Any that we don't do in class are homework
A notational helper.
Add Printing If bool.
Inductive prod (A B : Type) : Type := ...
Some notational helpers
Arguments pair {A B} _ _.
Add Printing Let prod.
Notation "x * y" := (prod x y) : type_scope.
Notation "( x , y , .. , z )" := (pair .. (pair x y) .. z) : core_scope.
Example prod_1 := (1, 1) : nat * nat.
Example prod_2 := (1, 2) : nat * nat.
Example prod_3 := (true, true) : bool * bool.
Example prod_4 := (1, true) : nat * bool.
(** We use curlie braces so that we don't have to pass the arguments explicitly all the time.
Definition fst
: ∀ {A B}, A × B → A
:= admit.
Definition snd
: ∀ {A B}, A × B → B
:= admit.
>> *)
sum A B, written A + B, is the disjoint sum of A and B
Inductive sum (A B : Type) : Type := ...
Notational helpers
Notation "x + y" := (sum x y) : type_scope.
Arguments inl {A B} _ , [A] B _.
Arguments inr {A B} _ , A [B] _.
Inductive sigT A (B : A -> Type) : Type := ...
Notational helpers
Arguments sigT {A} B.
Arguments existT {A} B _ _.
Notation "{ a : A & B }" := (sigT (A:=A) (fun a => B)) : type_scope.
Notation "{ a | B }" := ({ a : _ & B }) : type_scope.
Notation "{ a : A | B }" := ({ a : A & B }) : type_scope.
Notation "( a ; b )" := (existT _ a b).
Example non_dependent_pair_1 :=
(true; 0) : sigT (fun a : bool ⇒ nat).
Example non_dependent_pair_2 :=
(true; 1) : { a : bool | nat }.
Example non_dependent_pair_3 :=
(false; 1) : { a : bool | nat }.
Example dependent_pair_1 :=
(true; 1) : { a : bool | if a then nat else unit }.
Example dependent_pair_2 :=
(true; 0) : { a : bool | if a then nat else unit }.
Example dependent_pair_3 :=
(false; tt) : { a : bool | if a then nat else unit }.
Fail Example dependent_pair_4 :=
(false; 0) : { a : bool | if a then nat else unit }.
The projections
Notational helpers for the projections
Definition projT1
: forall {A B}, { a : A | B a } -> A
:= admit.
Definition projT2
: forall {A B} (x : { a : A | B a}), B (projT1 x)
:= admit.
Notation "x .1" := (projT1 x) (at level 3, format "x '.1'").
Notation "x .2" := (projT2 x) (at level 3, format "x '.2'").
Inductive option (A : Type) : Type := ...
Inductive list (A : Type) : Type := ...
Function types also have intro and elim rules, though they don't have syntactic forms. Can you describe them?
Note well: J is the eliminator for the equality type.
What equalities of types are provable?
Provable equalities
Provable inequalities
Isomorphisms
Class IsIsomorphism {A B} (f : A → B)
:= { iso_inv : B → A;
right_inv : ∀ x : B, f (iso_inv x) = x;
left_inv : ∀ x : A, iso_inv (f x) = x }.
Arguments iso_inv {A B} f {_} _.
Arguments right_inv {A B f _ _}, {A B} f {_} _.
Arguments left_inv {A B f _ _}, {A B} f {_} _.
Record Isomorphic A B
:= { iso_fun : A → B;
iso_isiso : IsIsomorphism iso_fun }.
Arguments iso_fun {A B} _ _.
Let us use an object of type Isomorphic as a function:
Tell Coq that the function associated to an Isomorphic object is always an isomorphism.
Existing Instance iso_isiso.
Notation "A <~=~> B" := (Isomorphic A B) (at level 70).
Notation "A ≅ B" := (Isomorphic A B) (at level 70).
We can prove the standard properties about isomorphisms:
Definition Isomorphic_refl : ∀ {A}, A ≅ A
:= admit.
Definition Isomorphic_inverse : ∀ {A B}, A ≅ B → B ≅ A
:= admit.
Definition Isomorphic_compose : ∀ {A B C}, A ≅ B → B ≅ C → A ≅ C
:= admit.
We would like to prove the last corresponding law:
But it's not provable! Here's a counter-example:
Recall the taboo from earlier; we can't prove equality of two identical types defined separately. But if we could prove Isomorphic_ap, then we could prove this!
Definition iso_unit2_unit1 : unit2 ≅ unit1
:= admit.
Definition taboo1 : unit1 = unit2 :> Type
:= admit.
Ooops! We'll be coming back to this soon.
More practice: You can prove the higher groupoid laws about isomorphisms, but it's a bit of a pain.
Before dealing with the taboo above, let's classify the equality space of isomorphisms.
We can classify the equality types. For each type, we come up with a simpler type that represents ("codes for") its equality type. Then we prove that this type is isomorphic to the given equality type. The exercises here are all to help understand how the encode-decode method works, to build up to function extensionality and univalence, and, for sigma types, to solve the first puzzle. We'll do them as needed to explain the method; the rest are (optional) homework. There are more exercises at the bottom of the file.
Equality classification
unit
Definition unit_encode : ∀ {x y : unit}, x = y → unit_code x y
:= admit.
Definition unit_decode : ∀ {x y : unit}, unit_code x y → x = y
:= admit.
Definition unit_endecode : ∀ {x y} (p : unit_code x y),
unit_encode (unit_decode p) = p
:= admit.
Definition unit_deencode : ∀ {x y} (p : x = y),
unit_decode (unit_encode p) = p
:= admit.
Definition unit_iso : ∀ x y : unit, (x = y) ≅ unit_code x y
:= fun x y
⇒ {| iso_fun := unit_encode;
iso_isiso := {| iso_inv := unit_decode;
right_inv := unit_endecode;
left_inv := unit_deencode |} |}.
Definition bool_code : ∀ (x y : bool), Type
:= admit.
Definition bool_encode : ∀ {x y : bool}, x = y → bool_code x y
:= admit.
Definition bool_decode : ∀ {x y : bool}, bool_code x y → x = y
:= admit.
Definition bool_endecode : ∀ {x y : bool} (p : bool_code x y),
bool_encode (bool_decode p) = p
:= admit.
Definition bool_deencode : ∀ {x y : bool} (p : x = y),
bool_decode (bool_encode p) = p
:= admit.
Definition Empty_set_code : ∀ (x y : Empty_set), Type
:= admit.
Definition Empty_set_encode : ∀ {x y : Empty_set},
x = y → Empty_set_code x y
:= admit.
Definition Empty_set_decode : ∀ {x y : Empty_set},
Empty_set_code x y → x = y
:= admit.
Definition Empty_set_endecode : ∀ {x y : Empty_set} (p : Empty_set_code x y),
Empty_set_encode (Empty_set_decode p) = p
:= admit.
Definition Empty_set_deencode : ∀ {x y : Empty_set} (p : x = y),
Empty_set_decode (Empty_set_encode p) = p
:= admit.
Definition arrow_code : ∀ {A B} (f g : A → B), Type
:= admit.
Definition arrow_encode : ∀ {A B} {f g : A → B}, f = g → arrow_code f g
:= admit.
The rest aren't currently provable in Coq; it's the axiom of functional extensionality.
Axiom arrow_decode : ∀ {A B} {f g : A → B}, arrow_code f g → f = g.
Axiom arrow_endecode : ∀ {A B} {f g : A → B} (p : arrow_code f g),
arrow_encode (arrow_decode p) = p.
Axiom arrow_deencode : ∀ {A B} {f g : A → B} (p : f = g),
arrow_decode (arrow_encode p) = p.
Definition function_code : ∀ {A B} (f g : ∀ a : A, B a), Type
:= admit.
Definition function_encode : ∀ {A B} {f g : ∀ a : A, B a}, f = g → function_code f g
:= admit.
The rest aren't currently provable in Coq; it's the axiom of functional extensionality.
Axiom function_decode : ∀ {A B} {f g : ∀ a : A, B a}, function_code f g → f = g.
Axiom function_endecode : ∀ {A B} {f g : ∀ a : A, B a} (p : function_code f g),
function_encode (function_decode p) = p.
Axiom function_deencode : ∀ {A B} {f g : ∀ a : A, B a} (p : f = g),
function_decode (function_encode p) = p.
Definition sigma_code : ∀ {A B} (x y : { a : A | B a }), Type
:= admit.
Definition sigma_encode : ∀ {A B} {x y : { a : A | B a }}, x = y → sigma_code x y
:= admit.
Definition sigma_decode : ∀ {A B} {x y : { a : A | B a }}, sigma_code x y → x = y
:= admit.
Definition sigma_endecode : ∀ {A B} {x y : { a : A | B a }} (p : sigma_code x y),
sigma_encode (sigma_decode p) = p
:= admit.
Definition sigma_deencode : ∀ {A B} {x y : { a : A | B a }} (p : x = y),
sigma_decode (sigma_encode p) = p
:= admit.
Back to isomorphisms.
Definition iso_code : ∀ {A B} (x y : A ≅ B), Type
:= admit.
Definition iso_encode : ∀ {A B} {x y : A ≅ B}, x = y → iso_code x y
:= admit.
Definition iso_decode : ∀ {A B} {x y : A ≅ B}, iso_code x y → x = y
:= admit.
Definition iso_endecode : ∀ {A B} {x y : A ≅ B} (p : iso_code x y),
iso_encode (iso_decode p) = p
:= admit.
Definition iso_deencode : ∀ {A B} {x y : A ≅ B} (p : x = y),
iso_decode (iso_encode p) = p
:= admit.
Ooops. Turns out this isn't provable. Challenge: Figure out why.
We can define a slight variation on isomorphisms, called "contractible fibers", which generalizes the notion of injective+surjective. If you're interested in the various ways of formulating equivalences, Chapter 4 of the HoTT Book (http://homotopytypetheory.org/book/) is an excellent resource.
Equivalences
Class Contr (A : Type)
:= { center : A;
contr : ∀ y, center = y }.
Arguments center A {_}.
Class IsEquiv {A B} (f : A → B)
:= Build_IsEquiv : ∀ b, Contr { a : A | f a = b }.
Record Equiv A B
:= { equiv_fun : A → B;
equiv_isequiv : IsEquiv equiv_fun }.
Arguments equiv_fun {A B} _ _.
Arguments equiv_isequiv {A B} _ _.
Let us use an object of type Equiv as a function:
Tell Coq that the function associated to an Equiv object is
always an equivalence.
Existing Instance equiv_isequiv.
Notation "A <~> B" := (Equiv A B) (at level 70).
Notation "A ≃ B" := (Equiv A B) (at level 70).
We can prove that an equivalence gives us an isomorphism very easily.
We can go the other way with more work.
Optional Homework: Complete this proof.
Now, we prove the following helper lemma, which lets us get the right codes for Equiv. We again assume functional extensionality.
Definition allpath_contr : ∀ {A} (x y : Contr A), x = y.
Proof.
refine admit.
Defined.
Definition allpath_isequiv : ∀ {A B} (f : A → B) (e1 e2 : IsEquiv f), e1 = e2.
Proof.
refine admit.
Defined.
Definition equiv_code : ∀ {A B} (f g : A ≃ B), Type
:= admit.
Definition equiv_encode : ∀ {A B} {f g : A ≃ B}, f = g → equiv_code f g
:= admit.
Definition equiv_decode : ∀ {A B} {f g : A ≃ B}, equiv_code f g → f = g
:= admit.
Definition equiv_endecode : ∀ {A B} {f g : A ≃ B} (p : equiv_code f g),
equiv_encode (equiv_decode p) = p
:= admit.
Definition equiv_deencode : ∀ {A B} {f g : A ≃ B} (p : f = g),
equiv_decode (equiv_encode p) = p
:= admit.
Now that we have a "good" type of isomorphism/equivalence (one with the right equality type), we can go back to the question of Isomorphic_ap; Recall that we want to prove:
We can prove this by axiomatizing the codes for types:
Definition Isomorphic_ap : forall (f : Type -> Type) {A B}, A ≅ B -> f A ≅ f B.
Definition Type_code' : ∀ (x y : Type), Type
:= fun x y ⇒ x ≃ y.
Definition Type_encode' : ∀ {x y : Type}, x = y → Type_code' x y
:= admit.
The following are unprovable in Coq, currently. They are collectively known as the "univalence axiom".
Axiom Type_decode' : ∀ {x y : Type}, Type_code' x y → x = y.
Axiom Type_endecode' : ∀ {x y : Type} (p : Type_code' x y),
Type_encode' (Type_decode' p) = p.
Axiom Type_deencode' : ∀ {x y : Type} (p : x = y),
Type_decode' (Type_encode' p) = p.
Definition Univalence : ∀ {x y : Type}, IsEquiv (@Type_encode' x y)
:= admit.
Definition Empty_set_eq : (Empty_set = Empty_set) = unit :> Type
:= admit.
Definition unit_eq : (unit = unit) = unit :> Type
:= admit.
Definition bool_eq : (bool = bool) = bool :> Type
:= admit.
Definition bool_arrow_bool_eq : (bool → bool) = (bool × bool)%type
:= admit.
Definition prod_commutes : ∀ (A B : Type), (A × B = B × A)%type
:= admit.
Challenge: Show, without axioms, that univalence implies functional extensionality:
Definition univalence_implies_funext
: (∀ A B, IsEquiv (@Type_encode' A B))
→ (∀ A B (f g : ∀ a : A, B a), (∀ a, f a = g a) → f = g)
:= admit.
Exercise 2.17 from the HoTT Book (http://homotopytypetheory.org/book/ - don't worry about reading the book):
Show that if A ≃ A' and B ≃ B', then (A × B) ≃ (A' × B') in two ways: once using univalence, and once without assuming it.
Definition equiv_functor_prod_univalence
: (∀ A B, IsEquiv (@Type_encode A B))
→ ∀ A A' B B',
A ≃ A' → B ≃ B' → (A × B ≃ A' × B')%type
:= admit.
Definition equiv_functor_prod_no_univalence
: ∀ A A' B B',
A ≃ A' → B ≃ B' → (A × B ≃ A' × B')%type
:= admit.
Now prove that these two ways are equal
Definition equiv_functor_prod_eq
: ∀ univalence,
equiv_functor_prod_univalence univalence = equiv_functor_prod_no_univalence
:= admit.
Definition prod_code : ∀ {A B} (x y : A × B), Type
:= admit.
Definition prod_encode : ∀ {A B} {x y : A × B}, x = y → prod_code x y
:= admit.
Definition prod_decode : ∀ {A B} {x y : A × B}, prod_code x y → x = y
:= admit.
Definition prod_endecode : ∀ {A B} {x y : A × B} (p : prod_code x y),
prod_encode (prod_decode p) = p
:= admit.
Definition prod_deencode : ∀ {A B} {x y : A × B} (p : x = y),
prod_decode (prod_encode p) = p
:= admit.
Definition sum_code : ∀ {A B} (x y : A + B), Type
:= admit.
Definition sum_encode : ∀ {A B} {x y : A + B}, x = y → sum_code x y
:= admit.
Definition sum_decode : ∀ {A B} {x y : A + B}, sum_code x y → x = y
:= admit.
Definition sum_endecode : ∀ {A B} {x y : A + B} (p : sum_code x y),
sum_encode (sum_decode p) = p
:= admit.
Definition sum_deencode : ∀ {A B} {x y : A + B} (p : x = y),
sum_decode (sum_encode p) = p
:= admit.
Hint for the above: Use J.
Definition zero_ne_succ : ∀ n, 0 = S n → Empty_set
:= admit.
Definition nat_code : ∀ (x y : nat), Type
:= admit.
Definition nat_encode : ∀ {x y : nat},
x = y → nat_code x y
:= admit.
Definition nat_decode : ∀ {x y : nat},
nat_code x y → x = y
:= admit.
Definition nat_endecode : ∀ {x y : nat} (p : nat_code x y),
nat_encode (nat_decode p) = p
:= admit.
Definition nat_deencode : ∀ {x y : nat} (p : x = y),
nat_decode (nat_encode p) = p
:= admit.
Definition option_code : ∀ {A} (x y : option A), Type
:= admit.
Definition option_encode : ∀ {A} {x y : option A}, x = y → option_code x y
:= admit.
Definition option_decode : ∀ {A} {x y : option A}, option_code x y → x = y
:= admit.
Definition option_endecode : ∀ {A} {x y : option A} (p : option_code x y),
option_encode (option_decode p) = p
:= admit.
Definition option_deencode : ∀ {A} {x y : option A} (p : x = y),
option_decode (option_encode p) = p
:= admit.
Definition list_code : ∀ {A} (x y : list A), Type
:= admit.
Definition list_encode : ∀ {A} {x y : list A}, x = y → list_code x y
:= admit.
Definition list_decode : ∀ {A} {x y : list A}, list_code x y → x = y
:= admit.
Definition list_endecode : ∀ {A} {x y : list A} (p : list_code x y),
list_encode (list_decode p) = p
:= admit.
Definition list_deencode : ∀ {A} {x y : list A} (p : x = y),
list_decode (list_encode p) = p
:= admit.
Homework: mere propositions
We classify the equality type of propositions.
Definition prop_code : ∀ {A} (allpaths : is_prop A) (x y : A), Type
:= fun A allpaths x y ⇒ unit.
Definition prop_encode : ∀ {A} (allpaths : is_prop A) {x y : A},
x = y → prop_code allpaths x y
:= admit.
Definition prop_decode : ∀ {A} (allpaths : is_prop A) {x y : A},
prop_code allpaths x y → x = y
:= admit.
Definition prop_endecode : ∀ {A} (allpaths : is_prop A) {x y : A} (p : prop_code allpaths x y),
prop_encode allpaths (prop_decode allpaths p) = p
:= admit.
If you find this proof hard, and can't figure out why, think about the proof of dec_deencode. If you're still having trouble, look a bit further down for a hint.
Definition prop_deencode : ∀ {A} (allpaths : is_prop A) {x y : A} (p : x = y),
prop_decode allpaths (prop_encode allpaths p) = p
:= admit.
Hint: you may need to rewrite your prop_decode function. The following lemmas may prove helpful.
Try to write an "adjuster" for is_prop that will always return something equal to refl (provably) when handed two judgmentally equal things.
Definition adjust_allpaths : ∀ {A}, is_prop A → is_prop A
:= admit.
Definition adjust_allpaths_refl : ∀ {A} (allpaths : is_prop A) x,
adjust_allpaths allpaths x x = refl
:= admit.
Now move these lemmas above prop_decode and try re-writing the codes so that you expect prop_deencode to work.
Homework; challenging problem
More generally, we can do this for any type with decidable equality.
First we define what it means for a type to have decidable equality: it means that we have a (continuous!) function from pairs of inhabitants to proofs that either they are equal, or that their equality is absurd.
decidable types
Definition decidable : Type → Type
:= fun A ⇒ ∀ x y : A, (x = y) + (x = y → Empty_set).
Definition dec_code : ∀ {A} (dec : decidable A) (x y : A), Type
:= admit.
Definition dec_encode : ∀ {A} (dec : decidable A) {x y : A},
x = y → dec_code dec x y
:= admit.
Definition dec_decode : ∀ {A} (dec : decidable A) {x y : A},
dec_code dec x y → x = y
:= admit.
Definition dec_endecode : ∀ {A} (dec : decidable A) {x y : A} (p : dec_code dec x y),
dec_encode dec (dec_decode dec p) = p
:= admit.
If you find this proof hard, and can't figure out why, look a bit further down for a hint.
Definition dec_deencode : ∀ {A} (dec : decidable A) {x y : A} (p : x = y),
dec_decode dec (dec_encode dec p) = p
:= admit.
Hint: you may need to rewrite your dec_decode, dec_code, and dec_encode functions, just as you did with prop_decode. The following lemmas may prove helpful.
Try to write an "adjuster" for decidable equality that will always return something equal to refl (provably) when handed two equal things.
Definition adjust_dec : ∀ {A}, decidable A → decidable A
:= admit.
Definition adjust_dec_refl : ∀ {A} (dec : decidable A) (x : A),
adjust_dec dec x x = inl refl
:= admit.
Now move these lemmas above dec_code and try re-writing the codes so that you expect dec_deencode to work.
Homework: Generalize the above two proofs to solve "Puzzle 2" far above. (Don't forget to also do puzzle 1 while you're at it.)
Homework: Using the solution to Puzzle 2, show that it is sufficient to assume function_decode an axiom; the endecode and deencode proofs follow from puzzle 2.
Pushing Further
Definition function_code' : ∀ {A B} (f g : ∀ a : A, B a), Type
:= admit.
Definition function_encode' : ∀ {A B} {f g : ∀ a : A, B a}, f = g → function_code' f g
:= admit.
Axiom function_decode' : ∀ {A B} {f g : ∀ a : A, B a}, function_code' f g → f = g.
Definition function_decode_adjusted' : ∀ {A B} {f g : ∀ a : A, B a}, function_code' f g → f = g
:= admit.
Definition function_endecode' : ∀ {A B} {f g : ∀ a : A, B a} (p : function_code' f g),
function_encode' (function_decode_adjusted' p) = p
:= admit.
Definition function_deencode' : ∀ {A B} {f g : ∀ a : A, B a} (p : f = g),
function_decode_adjusted' (function_encode' p) = p
:= admit.