Equality of dependent types and dependent values - equality

Consider a dependent type
Inductive dep (n: nat) :=
mkDep : dep n.
Now, consider a simple theorem I wish to prove:
Theorem equalTypes (n n': nat): n = n' -> dep n = dep n'.
Proof.
intros.
Abort.
How do I show that two dependent types are equal? What is a notion of type equality?
Worse, consider this "theorem" (which does not compile)
Theorem equalInhabitants (n n' : nat): n = n' -> mkDep n = mkDep n'.
Abort.
This very statement is wrong, because the types mkDep n and mkDep n' don't match. However, in some sense, this statement is true, because they are the same value under the assumption n = n'.
I wish to understand how to formalize and prove statements about dependent types (specifically, their equality and notions thereof)

How do I show that two dependent types are equal?
In this case, you can prove it with apply f_equal; assumption or subst; reflexivity (or destruct H; reflexivity or case H; reflexivity or induction H; reflexivity or exact (eq_rect n (fun n' => dep n = dep n') eq_refl n' H)).
What is a notion of type equality?
The same as any other equality; Print eq. gives:
Inductive eq (A : Type) (x : A) : A -> Prop := eq_refl : x = x
which says that the only special fact you have to construct a proof of equality is that x = x for any x. The way to use a proof of equality, eq_rect, is that, if you have x = y, to prove a property P of y, it suffices to prove P of x. In this case, since we have n = n', to prove dep n = dep n', it suffices to prove dep n = dep n (where P := fun n' => dep n = dep n').
There is a deeper sense in which this question can be asked, because it turns out that equality of types in Coq is under-constrained. Given
Inductive unit1 := tt1.
Inductive unit2 := tt2.
you can not prove unit1 = unit2, nor can you prove unit1 <> unit2. In fact, it turns out that the only type inequalities T1 <> T2 that you can prove are cases where you can prove that T1 and T2 are not isomorphic. The Univalence axiom is a way of "filling in the details" of type equality to say that any isomorphic types are equal. There are other consistent interpretations, though (for example, I believe that it's consistent to assume A * B = C * D -> A = C /\ B = D, though this contradicts univalence).
Worse, consider this "theorem" (which does not compile)
Theorem equalInhabitants (n n' : nat): n = n' -> mkDep n = mkDep n'.
Right. This is because we do not have an equality reflection rule in Coq, and judgmental/definitional equality is not the same as propositional equality. The way to state this is to "cast" the term mkDep n across the proof of equality.
Import EqNotations.
Theorem equalInhabitants (n n' : nat): forall H : n = n', rew H in mkDep n = mkDep n'.
intros.
subst; reflexivity.
Qed.
Note that rew binds more tightly than =, and is a notation for eq_rect. This says that for any proof H of n = n', the term mkDep n, when transported across H to become a term of type dep n', is equal to mkDep n'. (Note also that we could just as well have used destruct H or induction H or case H (but not apply f_equal) instead of subst.)

Related

Obtain decidable total order on a type from an injection into `nat`

Since the natural numbers support a decidable total order, the injection nat_of_ascii (a : ascii) : nat induces a decidable total order on the type ascii. What would be a concise, idiomatic way of expressing this in Coq? (With or without type classes, modules, etc.)
Such process is fairly routine and will depend on the library you have chosen. For order.v, based on math-comp, the process is totally mechanical [in fact, we'll develop a general construction for types with an injection to total orders later in the post]:
From Coq Require Import Ascii String ssreflect ssrfun ssrbool.
From mathcomp Require Import eqtype choice ssrnat.
Require Import order.
Import Order.Syntax.
Import Order.Theory.
Lemma ascii_of_natK : cancel nat_of_ascii ascii_of_nat.
Proof. exact: ascii_nat_embedding. Qed.
(* Declares ascii to be a member of the eq class *)
Definition ascii_eqMixin := CanEqMixin ascii_of_natK.
Canonical ascii_eqType := EqType _ ascii_eqMixin.
(* Declares ascii to be a member of the choice class *)
Definition ascii_choiceMixin := CanChoiceMixin ascii_of_natK.
Canonical ascii_choiceType := ChoiceType _ ascii_choiceMixin.
(* Specific stuff for the order library *)
Definition ascii_display : unit. Proof. exact: tt. Qed.
Open Scope order_scope.
(* We use the order from nat *)
Definition lea x y := nat_of_ascii x <= nat_of_ascii y.
Definition lta x y := ~~ (lea y x).
Lemma lea_ltNeq x y : lta x y = (x != y) && (lea x y).
Proof.
rewrite /lta /lea leNgt negbK lt_neqAle.
by rewrite (inj_eq (can_inj ascii_of_natK)).
Qed.
Lemma lea_refl : reflexive lea.
Proof. by move=> x; apply: le_refl. Qed.
Lemma lea_trans : transitive lea.
Proof. by move=> x y z; apply: le_trans. Qed.
Lemma lea_anti : antisymmetric lea.
Proof. by move=> x y /le_anti /(can_inj ascii_of_natK). Qed.
Lemma lea_total : total lea.
Proof. by move=> x y; apply: le_total. Qed.
(* We can now declare ascii to belong to the order class. We must declare its
subclasses first. *)
Definition asciiPOrderMixin :=
POrderMixin lea_ltNeq lea_refl lea_anti lea_trans.
Canonical asciiPOrderType := POrderType ascii_display ascii asciiPOrderMixin.
Definition asciiLatticeMixin := Order.TotalLattice.Mixin lea_total.
Canonical asciiLatticeType := LatticeType ascii asciiLatticeMixin.
Canonical asciiOrderType := OrderType ascii lea_total.
Note that providing an order instance for ascii gives us access to a large theory of total orders, plus operators, etc..., however the definition of total itself is fairly simple:
"<= is total" == x <= y || y <= x
where <= is a "decidable relation" and we assume, of course, decidability of equality for the particular type. Concretely, for an arbitrary relation:
Definition total (T: Type) (r : T -> T -> bool) := forall x y, r x y || r y x.
so if T is and order, and satisfies total, you are done.
More generally, you can define a generic principle to build such types using injections:
Section InjOrder.
Context {display : unit}.
Local Notation orderType := (orderType display).
Variable (T : orderType) (U : eqType) (f : U -> T) (f_inj : injective f).
Open Scope order_scope.
Let le x y := f x <= f y.
Let lt x y := ~~ (f y <= f x).
Lemma CO_le_ltNeq x y: lt x y = (x != y) && (le x y).
Proof. by rewrite /lt /le leNgt negbK lt_neqAle (inj_eq f_inj). Qed.
Lemma CO_le_refl : reflexive le. Proof. by move=> x; apply: le_refl. Qed.
Lemma CO_le_trans : transitive le. Proof. by move=> x y z; apply: le_trans. Qed.
Lemma CO_le_anti : antisymmetric le. Proof. by move=> x y /le_anti /f_inj. Qed.
Definition InjOrderMixin : porderMixin U :=
POrderMixin CO_le_ltNeq CO_le_refl CO_le_anti CO_le_trans.
End InjOrder.
Then, the ascii instance gets rewritten as follows:
Definition ascii_display : unit. Proof. exact: tt. Qed.
Definition ascii_porderMixin := InjOrderMixin (can_inj ascii_of_natK).
Canonical asciiPOrderType := POrderType ascii_display ascii ascii_porderMixin.
Lemma lea_total : #total ascii (<=%O)%O.
Proof. by move=> x y; apply: le_total. Qed.
Definition asciiLatticeMixin := Order.TotalLattice.Mixin lea_total.
Canonical asciiLatticeType := LatticeType ascii asciiLatticeMixin.
Canonical asciiOrderType := OrderType ascii lea_total.

Understanding 'impossible'

Type-Driven Development with Idris presents:
twoPlusTwoNotFive : 2 + 2 = 5 -> Void
twoPlusTwoNotFive Refl impossible
Is the above a function or value? If it's the former, then why is there no variable arguments, e.g.
add1 : Int -> Int
add1 x = x + 1
In particular, I'm confused at the lack of = in twoPlusTwoNotFive.
impossible calls out combinations of arguments which are, well, impossible. Idris absolves you of the responsibility to provide a right-hand side when a case is impossible.
In this instance, we're writing a function of type (2 + 2 = 5) -> Void. Void is a type with no values, so if we succeed in implementing such a function we should expect that all of its cases will turn out to be impossible. Now, = has only one constructor (Refl : x = x), and it can't be used here because it requires ='s arguments to be definitionally equal - they have to be the same x. So, naturally, it's impossible. There's no way anyone could successfully call this function at runtime, and we're saved from having to prove something that isn't true, which would have been quite a big ask.
Here's another example: you can't index into an empty vector. Scrutinising the Vect and finding it to be [] tells us that n ~ Z; since Fin n is the type of natural numbers less than n there's no value a caller could use to fill in the second argument.
at : Vect n a -> Fin n -> a
at [] FZ impossible
at [] (FS i) impossible
at (x::xs) FZ = x
at (x::xs) (FS i) = at xs i
Much of the time you're allowed to omit impossible cases altogether.
I slightly prefer Agda's notation for the same concept, which uses the symbol () to explicitly pinpoint which bit of the input expression is impossible.
twoPlusTwoNotFive : (2 + 2 ≡ 5) -> ⊥
twoPlusTwoNotFive () -- again, no RHS
at : forall {n}{A : Set} -> Vec A n -> Fin n -> A
at [] ()
at (x ∷ xs) zero = x
at (x ∷ xs) (suc i) = at xs i
I like it because sometimes you only learn that a case is impossible after doing some further pattern matching on the arguments; when the impossible thing is buried several layers down it's nice to have a visual aid to help you spot where it was.

Constraining input arguments to a function

Lets say I want to define the Fibonacci function as following function:
fibo : Int -> Int
fibo 1 = 1
fibo 2 = 2
fibo n = fibo (n-1) + fibo (n-2)
This function is obviously not total since its undefined for integers below 1, so I need to constrain the input argument somehow..
I've tried playing around with defining a new data type MyInt. Something along the lines:
-- bottom is the lower limit
data MyInt : (bottom: Int) -> (n: Int) -> Type
where
...
fibo : MyInt 1 n -> Int
...
However I get lost rather quickly.
How can I constraint the input argument to, for example, my fibo function to be integer values of 1 or above?
There are actually two reasons why Idris will not recognise the fibo function as total. Firstly, as you pointed out, it is not defined for integers less than 1, but secondly, it calls itself recursively. Although Idris is capable of recognising the totality of recursive functions, it can generally only do so when it can be shown that the argument to the recursive call is 'smaller' (i.e. closer to a base case*) than the original argument (for example, if a function receives a list as an argument, it can call itself with the tail of the list without necessarily sacrificing totality, because the tail is a substructure of the original list and thus closer to Nil). The problem with expressions like (n-1) and (n-2), when they are of type Int, is that although they are numerically smaller than n, they are not structurally smaller, because Int is not inductively defined and so has no base cases. Therefore the totality checker is unable to satisfy itself that the recursion will always eventually reach a base case (even though it might seem obvious to us), and so it will not consider fibo to be total.
First off, let's solve the recursion problem. Instead of Int, we can use an inductively-defined datatype such as Nat:
data Nat =
Z | S Nat
(A natural number is either zero, or the successor of another natural number.)
This allows us to rewrite fibo as:
fibo : Nat -> Int
fibo (S Z) = 1
fibo (S (S Z)) = 2
fibo (S (S n)) = fibo (S n) + fibo n
(Note how in the recursive case, instead of calculating (n-1) and (n-2) explicitly, we produce them by pattern matching on the argument, thereby demonstrating to Idris that they are structurally smaller.)
This new definition of fibo is still not entirely total, though, because it lacks a case for Z (i.e. zero). If we don't want to provide for such a case, then we need to give Idris some assurance that it will not be allowed to occur. One way we can do this is to require a proof that the argument to fibo is greater than or equal to one (or equivalently, one is less than or equal to the argument):
fibo : (n : Nat) -> LTE 1 n -> Int
fibo Z LTEZero impossible
fibo Z (LTESucc _) impossible
fibo (S Z) _ = 1
fibo (S (S Z)) _ = 2
fibo (S (S (S n))) _ = fibo (S (S n)) (LTESucc LTEZero) + fibo (S n) (LTESucc LTEZero)
LTE 1 n is the type whose values are proofs that 1 ≤ n (within the natural numbers). LTEZero represents the axiom that zero ≤ any natural number, and LTESucc represents the rule that if n ≤ m, then (successor of n) ≤ (successor of m). The impossible keyword indicates that a given case cannot occur. In the above definition, it is impossible for the first argument to fibo to be zero because there is no way to prove that 1 ≤ 0. For any other natural number n, we can prove that 1 ≤ n using (LTESucc LTEZero).
Now at last fibo is total, but it's rather cumbersome to have to provide it with an explicit proof that its argument is greater than or equal to 1. Luckily, we can mark the proof argument as auto implicit:
fibo : (n : Nat) -> {auto p : LTE 1 n} -> Int
fibo Z {p = LTEZero} impossible
fibo Z {p = (LTESucc _)} impossible
fibo (S Z) = 1
fibo (S (S Z)) = 2
fibo (S (S (S n))) = fibo (S (S n)) + fibo (S n)
Idris will now automatically find a proof that 1 ≤ n where possible, otherwise we will still be required to provide one ourselves.
* There may well be some codata-related subtleties that I'm glossing over here without realising, but this is the broad principle.

Struggling with rewrite tactic in Idris

I'm going through Terry Tao's real analysis textbook, which builds up fundamental mathematics from the natural numbers up. By formalizing as many of the proofs as possible, I hope to familiarize myself with both Idris and dependent types.
I have defined the following datatype:
data GE: Nat -> Nat -> Type where
Ge : (n: Nat) -> (m: Nat) -> GE n (n + m)
to represent the proposition that one natural number is greater than or equal to another.
I'm currently struggling to prove reflexivity of this relation, i.e. to construct the proof with signature
geRefl : GE n n
My first attempt was to simply try geRefl {n} = Ge n Z, but this has type Ge n (add n Z). To get this to unify with the desired signature, we obviously have to perform some kind of rewrite, presumably involving the lemma
plusZeroRightNeutral : (left : Nat) -> left + fromInteger 0 = left
My best attempt is the following:
geRefl : GE n n
geRefl {n} = x
where x : GE n (n + Z)
x = rewrite plusZeroRightNeutral n in Ge n Z
but this does not typecheck.
Can you please give a correct proof of this theorem, and explain the reasoning behind it?
The first problem is superficial: you are trying to apply the rewriting at the wrong place. If you have x : GE n (n + Z), then you'll have to rewrite its type if you want to use it as the definition of geRefl : GE n n, so you'd have to write
geRefl : GE n n
geRefl {n} = rewrite plusZeroRightNeutral n in x
But that still won't work. The real problem is you only want to rewrite a part of the type GE n n: if you just rewrite it using n + 0 = n, you'd get GE (n + 0) (n + 0), which you still can't prove using Ge n 0 : GE n (n + 0).
What you need to do is use the fact that if a = b, then x : GE n a can be turned into x' : GE n b. This is exactly what the replace function in the standard library provides:
replace : (x = y) -> P x -> P y
Using this, by setting P = GE n, and using Ge n 0 : GE n (n + 0), we can write geRefl as
geRefl {n} = replace {P = GE n} (plusZeroRightNeutral n) (Ge n 0)
(note that Idris is perfectly able to infer the implicit parameter P, so it works without that, but I think in this case it makes it clearer what is happening)

vector reflexivity under setoid equality using CoRN MathClasses

I have a simple lemma:
Lemma map2_comm: forall A (f:A->A->B) n (a b:t A n),
(forall x y, (f x y) = (f y x)) -> map2 f a b = map2 f b a.
which I was able to prove using standard equality (≡). Now I am need to prove the similar lemma using setoid equality (using CoRN MathClasses). I am new to this library and type classes in general and having difficulty doing so. My first attempt is:
Lemma map2_setoid_comm `{Equiv B} `{Equiv (t B n)} `{Commutative B A}:
forall (a b: t A n),
map2 f a b = map2 f b a.
Proof.
intros.
induction n.
dep_destruct a.
dep_destruct b.
simpl.
(here '=' is 'equiv'). After 'simpl' the goal is "(nil B)=(nil B)" or "[]=[]" using VectorNotations. Normally I would finish it using 'reflexivity' tactics but it gives me:
Tactic failure: The relation equiv is not a declared reflexive relation. Maybe you need to require the Setoid library.
I guess I need somehow to define reflexivity for vector types, but I am not sure how to do that. Please advise.
First of all the lemma definition needs to be adjusted to:
Lemma map2_setoid_comm : forall `{CO:Commutative B A f} `{SB: !Setoid B} ,
forall n:nat, Commutative (map2 f (n:=n)).
To be able to use reflexivity:
Definition vec_equiv `{Equiv A} {n}: relation (vector A n) := Vforall2 (n:=n) equiv.
Instance vec_Equiv `{Equiv A} {n}: Equiv (vector A n) := vec_equiv.