Reputation: 75
I was working through a simple lemma and came across a situation where neither the contradiction or injection tactics were as powerful as I might have expected.
Theorem list_app_single: forall (T : Type) (x : T) (l1 l2 : list T),
l1++[x] = l2++[x] -> l1 = l2.
Proof.
intros. generalize dependent l2. induction l1.
- intros. induction l2.
-- reflexivity.
-- inversion H.
Stopping here the proof state is:
1 subgoal
T : Type
x, x0 : T
l2 : list T
H : [ ] ++ [x] = (x0 :: l2) ++ [x]
IHl2 : [ ] ++ [x] = l2 ++ [x] -> [ ] = l2
H1 : x = x0
H2 : [ ] = l2 ++ [x0]
______________________________________(1/1)
[ ] = x0 :: l2
Clearly H2 is not true, but coq doesn't see this when applying the discriminate tactic. Is there a tactic that is more appropriate that can resolve that H2 is in fact false?
Edit: Made the question more focused on a specific tactic to overcome limitations of discriminate.
Upvotes: 0
Views: 148
Reputation: 5811
I'm partial to @Theo's solution. It is easy to start destructing terms to the left and to the right, but by doing that one starts to reason on a too low level - reasoning about the implementation of the data rather than about the properties, which makes it less likely for the proof to carry over to other situations.
So if you want just "one tactic that works" then destruct
is often useful. You can solve you goal with
now destruct l2.
But if you want your proof to reason in the theory of the things you reason about - lists - then this proof does just that.
enough (@length T [] <> 0) by auto.
rewrite H2, app_length, PeanoNat.Nat.add_comm; auto.
Upvotes: 1
Reputation: 5108
To complete @Andrey's answer, discriminate
indeed applies on equalities featuring different constructors like [] = x :: l
.
A solution to this problem can be achieved by adding another observation on top of your equality. Here for instance, you can observe that the lengths of such expressions wouldn't match:
From [] = l ++ [x]
you obtain length [] = length (l ++ [x])
which simplifies to 0 = length l + 1
.
From Coq Require Import List Lia.
Import ListNotations.
Lemma foo :
forall (A : Type) (l : list A) (x : A),
[] = l ++ [x] ->
False.
Proof.
intros A l x e.
apply (f_equal (@length A)) in e.
rewrite app_length in e. simpl in e. lia.
Qed.
Notice that in the end, I apply the lia
tactic to derive a contradiction from 0 = n + 1
.
This method can be very useful in cases where just destructing l
wouldn't work right away.
If you have discriminations that you perform often, then it might be worth it to have your own discr
tactic to do it for you.
Upvotes: 3
Reputation: 750
To use the discriminate
tactic, you need the "top" terms of parts your expression to be constructors (after simplifying). So, the discriminate
can see [after some 'thinking'] that the constructors are different.
You do not have constructor on the top of the right part of equality in H2
. Namely, l2 ++ [x0]
is not a constructor. It is an application of function app
(another name of ++
operator) which has two parameters, namely l2
and [x0]
. So, your expression looks like
H2 : [ ] = app l2 [x0]
And the right part is obviously not a constructor.
What you can try is to destruct l2.
. Since the app
function works by matching its first argument then after the simplifying (in H2), top right part of H2 will be a constructor. And the discriminate
will be able to finish the proof.
As for another, more smart tactic which able to do it: as far as I can see a general solution is impossible here (we should fall into the halting problem). But I think that after some research somebody could create something more smart then just discrimite
. You can see an example of such tactic with good explanation of how it works (the tactic is named crush
) in Certified Programming with Dependent Types book by Adam Chlipala.
Namely crush
might not be able to solve this particular problem. But I think you could write example of such tactic using techniques the author suggests.
Upvotes: 2