I read that the induction principle for a type is just a theorem about a proposition P. So I constructed an induction principle for List based on the right (or reverse) list constructor .
Definition rcons {X:Type} (l:list X) (x:X) : list X :=
l ++ x::nil.
The induction principle itself is:
Definition true_for_nil {X:Type}(P:list X -> Prop) : Prop :=
P nil.
Definition true_for_list {X:Type} (P:list X -> Prop) : Prop :=
forall xs, P xs.
Definition preserved_by_rcons {X:Type} (P: list X -> Prop): Prop :=
forall xs' x, P xs' -> P (rcons xs' x).
Theorem list_ind_rcons:
forall {X:Type} (P:list X -> Prop),
true_for_nil P ->
preserved_by_rcons P ->
true_for_list P.
Proof. Admitted.
But now, I am having trouble using the theorem. I don't how to invoke it to achieve the same as the induction tactic.
For example, I tried:
Theorem rev_app_dist: forall {X} (l1 l2:list X), rev (l1 ++ l2) = rev l2 ++ rev l1.
Proof. intros X l1 l2.
induction l2 using list_ind_rcons.
But in the last line, I got:
Error: Cannot recognize an induction scheme.
What are the correct steps to define and apply a custom induction principle like list_ind_rcons?
Thanks
If one would like to preserve the intermediate definitions, then one could use the Section mechanism, like so:
Require Import Coq.Lists.List. Import ListNotations.
Definition rcons {X:Type} (l:list X) (x:X) : list X :=
l ++ [x].
Section custom_induction_principle.
Variable X : Type.
Variable P : list X -> Prop.
Hypothesis true_for_nil : P nil.
Hypothesis true_for_list : forall xs, P xs.
Hypothesis preserved_by_rcons : forall xs' x, P xs' -> P (rcons xs' x).
Fixpoint list_ind_rcons (xs : list X) : P xs. Admitted.
End custom_induction_principle.
Coq substitutes the definitions and list_ind_rcons has the needed type and induction ... using ... works:
Theorem rev_app_dist: forall {X} (l1 l2:list X),
rev (l1 ++ l2) = rev l2 ++ rev l1.
Proof. intros X l1 l2.
induction l2 using list_ind_rcons.
Abort.
By the way, this induction principle is present in the standard library (List module):
Coq < Check rev_ind.
rev_ind
: forall (A : Type) (P : list A -> Prop),
P [] ->
(forall (x : A) (l : list A), P l -> P (l ++ [x])) ->
forall l : list A, P l
What you did was mostly correct. The problem is that Coq has some trouble recognizing that what you wrote is an induction principle, because of the intermediate definitions. This, for instance, works just fine:
Theorem list_ind_rcons:
forall {X:Type} (P:list X -> Prop),
P nil ->
(forall x l, P l -> P (rcons l x)) ->
forall l, P l.
Proof. Admitted.
Theorem rev_app_dist: forall {X} (l1 l2:list X), rev (l1 ++ l2) = rev l2 ++ rev l1.
Proof. intros X l1 l2.
induction l2 using #list_ind_rcons.
I don't know if Coq not being able to automatically unfold the intermediate definitions should be considered a bug or not, but at least there is a workaround.
Related
I'm new in coq. i am trying to prove that the subsequence of an empty list is empty
This is the lemma i'm working on:
Lemma sub_nil : forall l , subseq l nil <-> l=nil.
i tried to split so i can have
subseq l nil -> l = nil
and
l = nil -> subseq l nil
to prove the first one i tried an induction on l but i blocked when it comes to prove that
subseq (a :: l) nil -> a :: l = nil
thanks.
The tactic to use here is inversion. Paraphrasing the coq documentation for inversion! :
Given an inductive hypothesis (H:I t), then inversion applied to H derives for each possible constructor c i of (I t), all the necessary conditions that should hold for the instance (I t) to be proved by c i.
Assuming the subseq predicate is given as follows:
Inductive subseq {A:Type} : list A -> list A -> Prop :=
| SubNil : forall (l:list A), subseq nil l
| SubCons1 : forall (s l:list A) (x:A), subseq s l -> subseq s (x::l)
| SubCons2 : forall (s l: list A) (x:A), subseq s l -> subseq (x::s) (x::l).
The proof would be stuck here(exactly at the place you specified):
Lemma sub_nil2 : forall (A:Type) (l: list A) , subseq l nil <-> l=nil.
Proof.
split.
- destruct l eqn:E; intros.
* reflexivity.
(*Now unable to prove a::l0 = [] because the hypothesis: subseq (a :: l0) [] is absurd.*)
* inversion H.(*Coq reasons that this hypothesis is not possible and discharges the proof trivially*)
- intros. subst. apply SubNil.
Qed.
Note that I used the destruct tactic but the issue remains even with induction tactic.
The entire proof can be written cleanly as below:
Lemma sub_nil : forall (A:Type) (l: list A) , subseq l nil <-> l=nil.
Proof.
split; intros.
- inversion H. reflexivity.
- subst. apply SubNil.
Qed.
Inductive bar {X : Type} : list X -> Prop :=
| bar_nil : bar []
| bar_fst : forall x l, bar (rev l ++ l) -> bar (rev l ++ [x] ++ l)
| bar_snd : forall x l, bar (rev l ++ [x] ++ l) -> bar (rev l ++ [x; x] ++ l).
Axiom bar_surround :
forall X x (l : list X),
bar l -> bar ([x] ++ l ++ [x]).
Inductive list_last {X : Type} : list X -> Prop :=
| ll_nil : list_last []
| ll_snoc : forall l x, list_last l -> list_last (l ++ [x]).
Axiom ll_app :
forall X (a b : list X),
list_last a -> list_last b -> list_last (a ++ b).
Axiom ll_from_list :
forall {X} (l : list X),
list_last l.
Axiom app_head_eq :
forall X (a b c : list X),
a ++ c = b ++ c -> a = b.
Theorem foo :
forall X (l: list X), l = rev l -> bar l.
Proof.
intros.
induction l.
- constructor.
- assert (Hll := ll_from_list l).
inversion Hll.
+ apply (bar_fst x []). apply bar_nil.
+ rewrite <- H1 in H.
simpl in H.
rewrite rev_app_distr in H.
rewrite <- app_assoc in H.
simpl in H.
inversion H.
apply app_head_eq in H4.
apply bar_surround.
1 subgoal
X : Type
x : X
l, l0 : list X
x0 : X
H : x :: l0 ++ [x0] = x0 :: rev l0 ++ [x]
IHl : l = rev l -> bar l
Hll : list_last l
H0 : list_last l0
H1 : l0 ++ [x0] = l
H3 : x = x0
H4 : l0 = rev l0
______________________________________(1/1)
bar l0
I am only a step away from getting this exercise solved, but I do not know how to do the induction step. Note that IHl is useless here and replacing induction on l with induction on Hll would have a similar problem. In both cases, the inductive hypothesis would expect a call with a one step decrease while I need two - one with the item taken from both the start and the end of the list on both sides of the equality.
Consider that the type of the function I am trying to prove is forall X (l: list X), l = rev l -> bar l and I have l0 = rev l0 -> bar l0 in the goal here. l0 is a decreased argument thereby making the recursive call safe.
What should I do here?
You can prove the following inductive predicate:
Inductive delist {A : Type} : list A -> Prop :=
| delist_nil : delist []
| delist_one x : delist [x]
| delist_cons x y l : delist l -> delist (x :: l ++ [y])
.
Theorem all_delist {A} : forall xs : list A, delist xs.
Then in your final theorem, induction on delist xs will split into the cases you need.
Another solution is by strong induction on the length of the list:
Lemma foo_len X : forall (n : nat) (l: list X), length l <= n -> l = rev l -> bar l.
Proof.
induction n.
(* Nat.le_succ_r from the Arith module is useful here *)
...
Qed.
(* Final theorem *)
Theorem foo X : forall (l : list X), l = rev l -> bar l.
Proof.
intros; apply foo_len; auto.
Qed.
This is a more common and systematic principle than delist, but you will need to work more than the ad-hoc inductive type above to use the induction hypothesis in the main proof.
Here is how to implement the first part of what was suggested in the other answer. I can confirm that with this, solving the exercise is quite simple. That having said, I am interested how to solve the above using straightforward induction. Having to implement delist and its functions is more complicated than I'd prefer.
Inductive delist {A : Type} : list A -> Prop :=
| delist_nil : delist []
| delist_one x : delist [x]
| delist_wrap x y l : delist l -> delist (x :: l ++ [y]).
Theorem delist_cons {A} :
forall x (l : list A),
delist l -> delist (x :: l).
Proof.
intros.
generalize dependent x.
induction H; intros.
- constructor.
- replace [x; x0] with (x :: [] ++ [x0]).
2 : { reflexivity. }
+ apply delist_wrap with (l := []). constructor.
- replace (x0 :: x :: l ++ [y]) with (x0 :: (x :: l) ++ [y]).
2 : { reflexivity. }
constructor.
apply IHdelist.
Qed.
Theorem delist_from_list {A} :
forall l : list A,
delist l.
Proof.
induction l.
- constructor.
- assert (ll := ll_from_list l).
destruct ll.
+ constructor.
+ apply delist_cons. assumption.
Qed.
I'm currently working through the Logical Foundations book and I'm stuck on the last part of Exercise: 4 stars, advanced (subsequence) (subseq_trans).
Here is my definition for subseq:
Inductive subseq { X : Type } : list X -> list X -> Prop :=
| s1 : forall l, subseq [] l
| s2 : forall (x : X) (l l': list X), subseq l l' -> subseq l (x :: l')
| s3 : forall (x : X) (l l' : list X), subseq l l' -> subseq (x :: l) (x :: l').
And here is my proof for subseq_trans:
Theorem subseq_trans : forall (X : Type) (l1 l2 l3 : list X),
subseq l1 l2 -> subseq l2 l3 -> subseq l1 l3.
Proof.
intros X l1 l2 l3 H H'.
generalize dependent H.
generalize dependent l1.
induction H'.
{ intros l1 H. inversion H. apply s1. }
{ intros l1 H. apply s2. apply IHH'. apply H. }
{ intros l1 H. apply s2. apply IHH'. apply s2 in H. (* Unable to find an instance for the variable x. *) }
Here is the proof context before the failed apply:
1 subgoal
X : Type
x : X
l, l' : list X
H' : subseq l l'
IHH' : forall l1 : list X, subseq l1 l -> subseq l1 l'
l1 : list X
H : subseq l1 (x :: l)
______________________________________(1/1)
subseq l1 l
I have tried explicitly instantiating x like this:
apply s2 with (x:=x) in H
But that gives me:
No such bound variable x (possible names are: x0, l0 and l'0).
Thanks in advance.
As diagnosed by #tbrk, this is a renaming done by Coq in the presence of maximal implicit arguments (see this issue). This is due to the declaration of {X : Type} in the definition of subsequence.
One solution is to use # to turn all implicit arguments to non-implicit and avoid this renaming issue. This would give:
apply #s2 with (x:=x) in H.
You may find the eapply tactic useful to see what is going on.
...
{ intros l1 H. apply s2. apply IHH'. eapply s2 in H.
gives subseq l1 (?1 :: x :: l), where you can instantiate the ?1 with whatever you want, but, as you can now see, applying s2 forward from that assumption doesn't advance the proof.
Another possibility is to apply s2 to x and then to the assumption H:
apply (s2 x) in H.
I also find it strange that apply s2 with (x:=x) does not work. Coq seems to be doing some renaming behind the scenes, probably to avoid confusion with the x in the proof context. The following sequence applies without error:
rename x into y. apply s2 with (x:=y) in H.
I'm trying to demonstrate the difference in code generation between Coq Extraction mechanism and MAlonzo compiler in Agda. I came up with this simple example in Agda:
data Nat : Set where
zero : Nat
succ : Nat → Nat
data List (A : Set) : Set where
nil : List A
cons : A → List A → List A
length : ∀ {A} → List A → Nat
length nil = zero
length (cons _ xs) = succ (length xs)
data Fin : Nat → Set where
finzero : ∀ {n} → Fin (succ n)
finsucc : ∀ {n} → Fin n → Fin (succ n)
elemAt : ∀ {A} (xs : List A) → Fin (length xs) → A
elemAt nil ()
elemAt (cons x _) finzero = x
elemAt (cons _ xs) (finsucc n) = elemAt xs n
Direct translation to Coq (with absurd pattern emulation) yields:
Inductive Nat : Set :=
| zero : Nat
| succ : Nat -> Nat.
Inductive List (A : Type) : Type :=
| nil : List A
| cons : A -> List A -> List A.
Fixpoint length (A : Type) (xs : List A) {struct xs} : Nat :=
match xs with
| nil => zero
| cons _ xs' => succ (length _ xs')
end.
Inductive Fin : Nat -> Set :=
| finzero : forall n : Nat, Fin (succ n)
| finsucc : forall n : Nat, Fin n -> Fin (succ n).
Lemma finofzero : forall f : Fin zero, False.
Proof. intros a; inversion a. Qed.
Fixpoint elemAt (A : Type) (xs : List A) (n : Fin (length _ xs)) : A :=
match xs, n with
| nil, _ => match finofzero n with end
| cons x _, finzero _ => x
| cons _ xs', finsucc m n' => elemAt _ xs' n' (* fails *)
end.
But the last case in elemAt fails with:
File "./Main.v", line 26, characters 46-48:
Error:
In environment
elemAt : forall (A : Type) (xs : List A), Fin (length A xs) -> A
A : Type
xs : List A
n : Fin (length A xs)
a : A
xs' : List A
n0 : Fin (length A (cons A a xs'))
m : Nat
n' : Fin m
The term "n'" has type "Fin m" while it is expected to have type
"Fin (length A xs')".
It seems that Coq does not infer succ m = length A (cons A a xs'). What should I
tell Coq so it would use this information? Or am I doing something completely senseless?
Doing pattern matching is the equivalent of using the destruct tactic.
You won't be able to prove finofzero directly using destruct.
The inversion tactic automatically generates some equations before doing what destruct does.
Then it tries to do what discriminate does. The result is really messy.
Print finofzero.
To prove something like fin zero -> P you should change it to fin n -> n = zero -> P first.
To prove something like list nat -> P (more usually forall l : list nat, P l) you don't need to change it to list A -> A = nat -> P, because list's only argument is a parameter in its definition.
To prove something like S n <= 0 -> False you should change it to S n1 <= n2 -> n2 = 0 -> False first, because the first argument of <= is a parameter while the second one isn't.
In a goal f x = f y -> P (f y), to rewrite with the hypothesis you first need to change the goal to f x = z -> f y = z -> P z, and only then will you be able to rewrite with the hypothesis using induction, because the first argument of = (actually the second) is a parameter in the definition of =.
Try defining <= without parameters to see how the induction principle changes.
In general, before using induction on a predicate you should make sure it's arguments are variables. Otherwise information might be lost.
Conjecture zero_succ : forall n1, zero = succ n1 -> False.
Conjecture succ_succ : forall n1 n2, succ n1 = succ n2 -> n1 = n2.
Lemma finofzero : forall n1, Fin n1 -> n1 = zero -> False.
Proof.
intros n1 f1.
destruct f1.
intros e1.
eapply zero_succ.
eapply eq_sym.
eapply e1.
admit.
Qed.
(* Use the Show Proof command to see how the tactics manipulate the proof term. *)
Definition elemAt' : forall (A : Type) (xs : List A) (n : Nat), Fin n -> n = length A xs -> A.
Proof.
fix elemAt 2.
intros A xs.
destruct xs as [| x xs'].
intros n f e.
destruct (finofzero f e).
destruct 1.
intros e.
eapply x.
intros e.
eapply elemAt.
eapply H.
eapply succ_succ.
eapply e.
Defined.
Print elemAt'.
Definition elemAt : forall (A : Type) (xs : List A), Fin (length A xs) -> A :=
fun A xs f => elemAt' A xs (length A xs) f eq_refl.
CPDT has more about this.
Maybe things would be clearer if at the end of a proof Coq performed eta reduction and beta/zeta reduction (wherever variables occur at most once in scope).
I think your problem is similar to Dependent pattern matching in coq . Coq's match does not infer much, so you have to help it by providing the equality by hand.
I have the defined inductive types:
Inductive InL (A:Type) (y:A) : list A -> Prop :=
| InHead : forall xs:list A, InL y (cons y xs)
| InTail : forall (x:A) (xs:list A), InL y xs -> InL y (cons x xs).
Inductive SubSeq (A:Type) : list A -> list A -> Prop :=
| SubNil : forall l:list A, SubSeq nil l
| SubCons1 : forall (x:A) (l1 l2:list A), SubSeq l1 l2 -> SubSeq l1 (x::l2)
| SubCons2 : forall (x:A) (l1 l2:list A), SubSeq l1 l2 -> SubSeq (x::l1) (x::l2).
Now I have to prove a series of properties of that inductive type, but I keep getting stuck.
Lemma proof1: forall (A:Type) (x:A) (l1 l2:list A), SubSeq l1 l2 -> InL x l1 -> InL x l2.
Proof.
intros.
induction l1.
induction l2.
exact H0.
Qed.
Can some one help me advance.
In fact, it is easier to do an induction on the SubSet judgment directly.
However, you need to be as general as possible, so here is my advice:
Lemma proof1: forall (A:Type) (x:A) (l1 l2:list A),
SubSeq l1 l2 -> InL x l1 -> InL x l2.
(* first introduce your hypothesis, but put back x and In foo
inside the goal, so that your induction hypothesis are correct*)
intros.
revert x H0. induction H; intros.
(* x In [] is not possible, so inversion will kill the subgoal *)
inversion H0.
(* here it is straitforward: just combine the correct hypothesis *)
apply InTail; apply IHSubSeq; trivial.
(* x0 in x::l1 has to possible sources: x0 == x or x0 in l1 *)
inversion H0; subst; clear H0.
apply InHead.
apply InTail; apply IHSubSeq; trivial.
Qed.
"inversion" is a tactic that checks an inductive term and gives you all the possible way to build such a term !!without any induction hypothesis!!
It only gives you the constructive premices.
You could have done it directly by induction on l1 then l2, but you would have to construct by hand the correct instance of inversion because your induction hypothesis would have been really weak.
Hope it helps,
V.