We will use the standard definition of the finite sets:
Inductive fin : nat -> Set :=
| F1 : forall {n : nat}, fin (S n)
| FS : forall {n : nat}, fin n -> fin (S n).
Let us assume that we have some P : forall {m : nat} (x y : fin m) : Set (the
important thing is that both arguments of P are of the same type). For
demonstration purposes, let P be just:
Definition P {m : nat} (x y : fin m) := {x = y} + {x <> y}.
Now, we would like to write a custom function that compares two numbers:
Fixpoint feq_dec {m : nat} (x y : fin m) : P x y.
The idea is straightforward: we match on x and y, for x = F1, y = F1 we
trivially return equality, for x = FS x', y = FS y' we recursively call
the procedure for x' and y', for other cases we can trivially return
inequality.
A direct translation of this idea into Coq obviously fails:
refine (
match x, y return P x y with
| F1 _, F1 _ => _
| FS _ x', F1 _ => _
| F1 _, FS _ y' => _
| FS _ x', FS _ y' => _
end
).
(*
* The term "y0" has type "fin n0" while it is expected to have type "fin n".
*)
During the match on x and y we loose the type information so we cannot apply them to P.
The standard trick with passing type equality proof does not help here:
refine (
match x in fin mx, y in fin my return mx = my -> P x y with
| F1 _, F1 _ => _
| FS _ x', F1 _ => _
| F1 _, FS _ y' => _
| FS _ x', FS _ y' => _
end eq_refl
).
(*
* The term "y0" has type "fin my" while it is expected to have type "fin mx".
*)
So, maybe we can use that proof of equality to cast x have the same type as
y?
Definition fcast {m1 m2 : nat} (Heq : m1 = m2) (x : fin m1) : fin m2.
Proof.
rewrite <- Heq.
apply x.
Defined.
We also need to be able to get rid of the cast later on. However, I noticed that
fcast eq_refl x = x is not sufficient as we need to make it work with
arbitrary equivalence proof. I have found something called UIP that does exactly
what I need.
Require Import Coq.Program.Program.
Lemma fcast_void {m : nat} : forall (x : fin m) (H : m = m),
fcast H x = x.
Proof.
intros.
rewrite -> (UIP nat m m H eq_refl).
trivial.
Defined.
Now we are ready to finish the whole definition:
refine (
match x in fin mx, y in fin my
return forall (Hmx : m = mx) (Hmy : mx = my), P (fcast Hmy x) y with
| F1 _, F1 _ => fun Hmx Hmy => _
| FS _ x', F1 _ => fun Hmx Hmy => _
| F1 _, FS _ y' => fun Hmx Hmy => _
| FS _ x', FS _ y' => fun Hmx Hmy => _
end eq_refl eq_refl
); inversion Hmy; subst; rewrite fcast_void.
- left. reflexivity.
- right. intro Contra. inversion Contra.
- right. intro Contra. inversion Contra.
- destruct (feq_dec _ x' y') as [Heq | Hneq].
+ left. apply f_equal. apply Heq.
+ right. intro Contra. dependent destruction Contra. apply Hneq. reflexivity.
Defined.
It goes through! However, it doesn't evaluate to any useful value. For example
the following yields a term with five nested matches instead of a simple value
(in_right or in_left). I suspect the problem is with the UIP axiom that I
used.
Compute (@feq_dec 5 (FS F1) (FS F1)).
So in the end, the definition that I came up with is pretty much useless.
I have also tried doing nested matches using the convoy pattern instead of doing matching two values at
the same time but I hit the same obstacles: as soon as I do the matching on the second value, P stops being applicable to it. Can I do it some other way?