Reputation: 2137
I have the following reflect predicate:
Require Import mathcomp.ssreflect.all_ssreflect.
Inductive reflect (P : Prop) (b : bool) : Prop :=
| ReflectT (p : P) (e : b = true)
| ReflectF (np : ~ P) (e : b = false).
And I am trying to relate the boolean conjunction to the logical one and the following one-line proof goes through:
Lemma andP (b1 b2 : bool) : reflect (b1 /\ b2) (b1 && b2).
Proof.
case b1; case b2; constructor =>//; by case.
Qed.
However, I don't understand how the last ; by case.
is applied. When we examine the proof without the last ; by case.
:
Lemma andP (b1 b2 : bool) : reflect (b1 /\ b2) (b1 && b2).
Proof.
case b1; case b2; constructor =>//.
We get 6 subgoals (2 are trivially true):
6 subgoals (ID 45)
b1, b2 : bool
============================
true /\ false
subgoal 2 (ID 46) is:
true && false = true
subgoal 3 (ID 116) is:
false /\ true
subgoal 4 (ID 117) is:
false && true = true
subgoal 5 (ID 187) is:
false /\ false
subgoal 6 (ID 188) is:
false && false = true
I'm not sure how to proceed from here because they all are false
- how can we prove that? I tried doing . case.
individually but that doesn't work. How does ; by case
admit these subgoals all at once?
Thank you.
Upvotes: 3
Views: 567
Reputation: 23582
The behavior of sequential composition for tactics changed a bit in recent years. Nowadays, tactics like constructor
can backtrack while executing their continuation. Because your definition of reflect
is a bit different from the standard one, if you just call constructor
, Coq will immediately apply ReflectT
, leading to a stuck goal in three of the cases:
Lemma andP (b1 b2 : bool) : reflect (b1 /\ b2) (b1 && b2).
Proof.
case b1; case b2=> /=.
- constructor=> //.
- (* constructor. *) (* Stuck *)
When you use sequential composition, the constructor
tactic backtracks, correctly finding the ReflectF
constructor.
constructor; by try case.
- constructor; by try case.
- constructor; by try case.
Qed.
Upvotes: 3
Reputation: 15404
I'm not sure why you are getting 6 subgoals: case b1; case b2; constructor
produces 4 subgoals, corresponding to the four possible cases for combinations of boolean values:
true /\ true
subgoal 2 (ID 13) is:
~ (true /\ false)
subgoal 3 (ID 15) is:
~ (false /\ true)
subgoal 4 (ID 17) is:
~ (false /\ false)
The first one is considered trivial by //
.
Set Printing Coercions
will tell you that your subgoals or actually as follows:
is_true true /\ is_true true
subgoal 2 (ID 13) is:
~ (is_true true /\ is_true false)
subgoal 3 (ID 15) is:
~ (is_true false /\ is_true true)
subgoal 4 (ID 17) is:
~ (is_true false /\ is_true false)
Unfolding is_true
might help: case b1; case b2; constructor; rewrite /is_true.
:
true = true /\ true = true
subgoal 2 (ID 19) is:
~ (true = true /\ false = true)
subgoal 3 (ID 20) is:
~ (false = true /\ true = true)
subgoal 4 (ID 21) is:
~ (false = true /\ false = true)
The 3 last subgoal are of the form (_ /\ _) -> False
(because ~ P
stands for not P
, which unfolds to P -> False
.
Thus the case
tactic added after constructor
destructs the top assumptions turning the last three goals into the following:
true = true -> false = true -> False
subgoal 2 (ID 145) is:
false = true -> true = true -> False
subgoal 3 (ID 155) is:
false = true -> false = true -> False
Here, we have false = true
as one of the assumptions in each subgoal, meaning we got a contradiction that SSReflect can immediately recognize and finish the proof using the principle of explosion.
Upvotes: 1