I've been working on formalizing a Hilbert deductive system within Coq. I have the following definition for a term in first-order logic:
Inductive term : Type :=
| tvar : variable -> term
| tfunc {n} : function n -> Vector.t term n -> term.
Definition args := Vector.t term.
Note that function : nat -> Type
is a parameter of the section.
I find myself having to write the following very specific induction scheme for the type, in order to prove certain theorems (specifically, to prove theorems about substitution of a variable with another term):
Lemma term_dbl : forall (P : term -> term -> Prop),
(forall x r, P (tvar x) r) ->
(forall n (f : function n) a x, P (tfunc f a) (tvar x)) ->
(forall {n} (f g : function n) a b, Vector.Forall2 P a b -> P (tfunc f a) (tfunc g b)) ->
forall t t', P t t'.
By taking inspiration from the mutual induction scheme I already wrote, and the standard library member Vector.rect2
, I came up with the following almost-solution:
Fixpoint term_dbl (P : term -> term -> Prop)
(Hbase : forall x r, P (tvar x) r)
(Hcontra : forall {n} (f : function n) a x, P (tfunc f a) (tvar x))
(Hstep : forall {n} (f g : function n) a b, Vector.Forall2 P a b -> P (tfunc f a) (tfunc g b))
t t' {struct t} : P t t' :=
match t with
| tvar x => Hbase x t'
| tfunc f a => match t' with
| tvar x => Hcontra f a x
| tfunc g b => let fix term_dbl_fix {n} (a' b' : args n) : Vector.Forall2 P a' b' :=
match a' with
| Vector.nil _ => Vector.case0 _ Vector.Forall2_nil (b' : args 0)
| Vector.cons _ ha n' ta => Vector.caseS' b' (fun b'' => Vector.Forall2 P (Vector.cons _ ha _ ta) b'')
(fun hb tb => Vector.Forall2_cons ha hb ta tb (term_dbl P Hbase Hcontra Hstep ha hb) (term_dbl_fix ta tb))
end
in Hstep f g a b (term_dbl_fix a b)
end
end.
As far as I can tell, the only problem with the definition is one of unification. The error message is:
The term "b'" has type "args n1" while it is expected to have type "args 0".
in the term b' : args 0
. (I added the explicit type annotation for clarity; error appears without it). I assume there will be problems with the second branch as well, if the first did not error.
I would expect the pattern-match on a'
to prove that n = 0
, but this is not what happens. Is there a way to modify this function to be accepted by Coq? And/or, is there an alternative way to prove this induction scheme?
EDIT:
I've added a branch for functions of differing arities.
Fixpoint term_dbl (P : term -> term -> Prop)
(Hbase : forall x r, P (tvar x) r)
(Hcontra : forall {n} (f : function n) a x, P (tfunc f a) (tvar x))
(Hcontra' : forall m n (f : function m) (g : function n) a b, m <> n -> P (tfunc f a) (tfunc g b))
(Hstep : forall {n} (f g : function n) a b, Vector.Forall2 P a b -> P (tfunc f a) (tfunc g b))
t t' {struct t} : P t t' :=
match t with
| tvar x => Hbase x t'
| @tfunc m f a => match t' with
| tvar x => Hcontra f a x
| @tfunc n g b => match Nat.eq_dec m n with
| left eq_refl => let fix term_dbl_fix {n'} (a' b' : args n') : Vector.Forall2 P a' b' :=
match a' with
| Vector.nil _ => Vector.case0 _ Vector.Forall2_nil b'
| Vector.cons _ ha _ ta => Vector.caseS' b' (fun b'' => Vector.Forall2 P (Vector.cons _ ha _ ta) b'')
(fun hb tb => Vector.Forall2_cons ha hb ta tb (term_dbl P Hbase Hcontra Hstep ha hb) (term_dbl_fix ta tb))
end
in Hstep f g a b (term_dbl_fix a b)
| right contra => Hcontra' contra
end
end
end.
However, the specific error has not changed: cannot unify "n'" and "0"
.
EDIT 2:
Finally got it working:
Lemma term_dbl : forall (P : term -> term -> Prop),
(forall x r, P (tvar x) r) ->
(forall {n} (f : function n) a x, P (tfunc f a) (tvar x)) ->
(forall {m n} (f : function m) (g : function n) a b, m <> n -> P (tfunc f a) (tfunc g b)) ->
(forall {n} (f g : function n) a b, Vector.Forall2 P a b -> P (tfunc f a) (tfunc g b)) ->
forall t t', P t t'.
Proof.
intros P Hbase Hcontra Hcontra' Hstep.
refine (fix term_dbl_fix t t' {struct t} := match t with
| tvar x => _
| @tfunc m f a => _
end).
- apply Hbase.
- destruct t' as [y|n g].
+ apply Hcontra.
+ specialize (Nat.eq_dec m n) as E.
destruct E.
* destruct e.
apply Hstep.
clear f. clear g. clear t.
induction a.
-- apply (Vector.case0 _ (Vector.Forall2_nil P) t0).
-- apply (Vector.caseS' t0 _ (fun hb tb => Vector.Forall2_cons P h hb a tb (term_dbl_fix h hb) (IHa tb))).
* apply Hcontra'. assumption.
Qed.