Proof on booleans, false = true - coq

I currently am at chapter 5 of "Software foundations" but felt the need to get back to chapter one to clarify a couple of things. In particular there is an exercise I did not quite digested, in which we are asked to use destruct twice to prove a result on booleans. Here it is with names and other details changed.
Inductive bool: Type :=
|true: bool
|false: bool.
Definition fb (b1:bool) (b2:bool) : bool :=
match b1, b2 with
| false, false => false
| _, _ => true
end.
Theorem th: forall a b: bool,
fb a b = false -> b = false.
Proof.
intros [] [] H.
- rewrite <- H. reflexivity.
- reflexivity.
- rewrite <- H. reflexivity.
- reflexivity.
Qed.
When at the first tick, context and goal are both nonsense:
H : fb true true = false
______________________________________(1/1)
true = false
Second tick the hypothesis is false. Third tick is same kind of nonsense as first one. Only fourth tick is reasonable with:
H : fb false false = false
______________________________________(1/1)
false = false
I understand that by the rewrite rules, all these things do work. However I have the impression we are quitting the narrow path of truth for the wilderness of falsity. More precisely, and AFAIK, a false hypothesis can be made to prove ANY statement, true or false. Here we use it to prove that false = true, OK why not, but still that makes me feel somewhat uncomfortable. I would not have expected a proof assistant to allow this.
Elaborating a bit
In a typical proof by contradiction, I would pick an hypothesis at random, and derive the goal till I find either a tautology or a contradiction. I would then conclude whether my hypothesis was true or false.
What happens here, in cases 1 (same for case 3), Coq starts from an hypothesis that is false:
H : fb true true = false
applies it to a goal that is a contradiction:
true = false
and combines them to find a tautology.
That is not a way of reasoning I am aware of. That recalls student 'jokes' where starting with 0=1 any absurd result on natural numbers can be proven.
Followup
So this morning during my commute I was thinking about what I had just written above. I now believe that cases 1 and 3 are proper proofs by contradiction. Indeed H is false and we use it to prove a goal that is a false. Hypotheses (values of a and b) have to be rejected. What may have confused me is that using rewrite we are doing part of the way "backward", starting from the goal.
I am a bit undecided for case 2, which reads:
H : fb true false = false
______________________________________(1/1)
false = false
which is basically false -> true, a tautology under the "principle of explosion". I would not think that could be used so directly in a proof.
Oh well, not sure I completely understood what's under the hood, but trust in Coq is untouched. Gotta go on and return to chapter 5. Thanks all for your comments.

First of all, thanks for providing a self-contained code.
I understand your uneasiness proving a goal using rewrite when you know that what you really ought to do is to derive a contradiction from the hypotheses. That does not make the reasoning incorrect though. It is true that under such assumptions you can prove this goal.
However I also think that this does not make the proof script really readable. In your example, you are considering all possible cases and it happens that three out of these four are impossible. When we read your proof we cannot see that. To make it clear that you are in an impossible case, there are a few tactic which are useful to say "look, I am now going to prove a contradiction to rule out this case".
One of them is exfalso. It will replace the current goal by False (since anything can be derived from False, as mentioned by #ejgallego in a comment).
A second one is absurd to say "I am now going to prove some statement and its negation" (this is basically equivalent to proving False).
A third one which is enough in your case is discriminate. It tries to find in the hypotheses a contradictory equality, such as true = false.
Theorem th: forall a b: bool,
fb a b = false -> b = false.
Proof.
intros [] [] H.
- discriminate.
- discriminate.
- discriminate.
- reflexivity.
Qed.
Now, just so you know, discriminate and reflexivity are both tried by the easy tactic. Thus the following proof will work as well (but it does not show what is going on and thus falls out of the scope of this question):
Theorem th: forall a b: bool,
fb a b = false -> b = false.
Proof.
intros [] [] H; easy.
Qed.
and this is syntactic sugar for the same proof:
Theorem th: forall a b: bool,
fb a b = false -> b = false.
Proof.
now intros [] [] H.
Qed.

Related

How to prove an absurd rule in Coq

I have to prove the following statement, however I am getting the error The type of H is not inductive. when trying to use inversion statement:
How can I do that correctly?
Variables phi: Prop.
Section absurd_rule.
Hypothesis H1: phi -> true = false.
Lemma absurd_rule: ~phi.
Proof.
intro H.
inversion H.
End absurd_rule.
inversion is not the right rule to be used here, as there is nothing to invert on H : phi.
Let's take a pause after you intro H:
H1 : phi -> true = false
H : phi
______________________________________(1/1)
False
First of all, note that H1 H : true = false (simple application). Let's take advantage of it by writing apply H1 in H:
H : true = false
______________________________________(1/1)
False
Almost there! Now, it is necessarily contradictory that true = false, and that's because of conflicting constructors.
There is a dedicated rule to deal with such cases, namingly discriminate H.
No more subgoals, we are there. The full proof session looks like this:
Variables phi: Prop.
Section absurd_rule.
Hypothesis H1 : phi -> true = false.
Lemma absurd_rule : ~phi.
Proof.
intro H.
apply H1 in H.
discriminate H.
Qed.
Also, worth noting that you can use False to describe bottom (lowercase false is a different thing -- an inductive boolean, instead of an empty Prop).
A simpler version of your proof would be just
Variables phi: Prop.
Section absurd_rule.
Hypothesis H1 : phi -> False.
Lemma absurd_rule : ~phi.
Proof.
assumption.
Qed.
...which is not a big discovery, because you just assumed phi to be false.
After a second of reflection...
You can't "prove a rule". Rules are semantics of the language, laws you use to derive conclusions from assumptions. They are not "theorems" to be proven, but a sort of prior reasoning you use to manipulate sentences. They are even distinct from axioms, it's something on the meta level. You can prove an implication, but not a deduction rule.
Thus, the "solution" to your problem is just an identity. Your absurd_rule is just a lemma that not phi implies not phi, which is trivially true -- remember that in intuitionistic logic negation is very often defined as to imply false.

What does Proof. simpl. reflexivity. Qed. mean in Coq?

I'm reading the book Software Foundations and got stuck at the very beginning.
The author defined a boolean type and common operations:
Inductive bool: Type :=
| true
| false.
Definition orb (b1: bool) (b2: bool) : bool :=
match b1 with
| true => true
| false => b2
end.
So let's say we want to prove the correctness of the or function.
The author wrote a test followed by a proof:
Example test_orb1: (orb true false) = true.
Proof. simpl. reflexivity. Qed.
Could someone explain to me what simpl. reflexivity mean? Is there any other way we can prove this simple test?
simpl is a tactic evaluating the goal. In your case, after executing it, the goal will be left to true = true.
reflexivity is a tactic discharging goals of the shape x = x (in its simplest incarnation). What it does under the hood is to provide the proof term eq_refl : x = x as a solution to the current proof obligation.
Now, there are many ways to achieve this thing that ultimately will produce the same (rather trivial) proof eq_refl (try doing Print test_orb1.). First, the simpl operation is not needed because Coq will do some computations when applying a term (in particular when calling reflexivity). Second, you could obtain the same effect as reflexivity by calling constructor, apply eq_refl or refine eq_refl. These are tactics with different goals but that happen to coincide here.

Understanding Coq through a simple theorem proof

I'm very new to Coq, and now I'm stuck trying to understand how Coq "reasons" with a simple theorem:
Theorem andb_true_elim2 : forall b c : bool,
andb b c = true -> c = true.
I supposed that the proof would be just:
Proof.
intros b c.
destruct c.
- destruct b.
+ reflexivity.
+ reflexivity.
- destruct b.
+ reflexivity.
+ reflexivity.
Qed.
But it fails with:
In environtment
H: true && false = true
Unable to unify "true" with "false"
The subgoal failing is the third reflexivity, where c is false and b is true:
1 subgoal
______________________________________(1/1)
true && false = true -> false = true
Why is that? Wasn't it equivalent to an implication?
true && (false = true) -> (false = true)
true && false -> false
false -> false
true
There are a few issues with this proof. First, you misunderstood what Coq wanted; the actual goal was the following:
((true && false) = true) -> (false = true)
This does not follow by reflexivity, as the conclusion of this formula, false = true, is an equality between two syntactically different expressions.
Second, Coq does not simplify the operators -> and = in the manner you described. Coq's theory allows automatic simplification in a few select expressions, such as those defined by case analysis. For instance, && is syntactic sugar for the andb function, which is defined in the standard library as follows:
Definition andb b c :=
if b then c else false.
When Coq sees an expression such as false && true, it expands it to the equivalent if false then true else false, which in turn simplifies to the else branch true. You can test this by calling the simpl tactic on the problematic branch.
The -> and = operators, on the other hand, are defined differently: the first one is a primitive in the logic, whereas the other one is a so-called inductive proposition. Neither of those can be automatically simplified in Coq, because they express concepts that are in general not computable: for instance, we can use = to express the equality of two functions f and g that take infinitely many natural numbers as inputs. This question discusses this difference in more detail.
If you want, you can state your theorem in a purely computable way with alternate definitions of implication and equality:
Definition negb b := if b then false else true.
Definition eqb b c := if b then c else negb c.
Definition implb b c := if b then c else true.
Lemma test : forall b c, (implb (eqb (andb b c) true) (eqb c true))
= true.
Proof. intros [] []; reflexivity. Qed.
However, a statement like this is generally harder to use in Coq.

Adding complete disjunctive assumption in Coq

In mathematics, we often proceed as follows: "Now let us consider two cases, the number k can be even or odd. For the even case, we can say exists k', 2k' = k..."
Which expands to the general idea of reasoning about an entire set of objects by disassembling it into several disjunct subsets that can be used to reconstruct the original set.
How is this reasoning principle captured in coq considering we do not always have an assumption that is one of the subsets we want to deconstruct into?
Consider the follow example for demonstration:
forall n, Nat.Even n => P n.
Here we can naturally do inversion on Nat.Even n to get n = 2*x (and an automatically-false eliminated assumption that n = 2*x + 1). However, suppose we have the following:
forall n, P n
How can I state: "let us consider even ns and odd ns". Do I need to first show that we have decidable forall n : nat, even n \/ odd n? That is, introduce a new (local or global) lemma listing all the required subsets? What are the best practices?
Indeed, to reason about a splitting of a class of objects in Coq you need to show an algorithm splitting them, unless you want to reason classically (there is nothing wrong with that).
IMO, a key point is getting such decidability hypotheses "for free". For instance, you could implement odd : nat -> bool as a boolean function, as it is done in some libraries, then you get the splitting for free.
[edit]
You can use some slightly more convenient techniques for pattern matching, by enconding the pertinent cases as inductives:
Require Import PeanoNat Nat Bool.
CoInductive parity_spec (n : nat) : Type :=
| parity_spec_odd : odd n = true -> parity_spec n
| parity_spec_even: even n = true -> parity_spec n
.
Lemma parityP n : parity_spec n.
Proof.
case (even n) eqn:H; [now right|left].
now rewrite <- Nat.negb_even, H.
Qed.
Lemma test n : even n = true \/ odd n = true.
Proof. now case (parityP n); auto. Qed.

Coq can't discriminate between constructors for dependently typed inductive proposition

I created this example type to demonstrate the problem I'm having:
Inductive foo : nat -> Prop :=
| foo_1 : forall n, foo n
| foo_2 : forall n, foo n.
Now clearly foo_1 0 <> foo_2 0, but I'm unable to prove this:
Lemma bar : foo_1 0 <> foo_2 0.
Proof. unfold not. intros H. discriminate H.
This returns the error
Not a discriminable equality.
inversion H doesn't change the context at all. Oddly, if I change foo from Prop to Type then the proof goes through, but I can't do this in my actual code because it causes problems elsewhere.
How can I get this proof to go through? And why is this problematic in the first place?
The logic underlying Coq is compatible with the axiom of "proof irrelevance" which states that any two proofs of a given Prop are equal. As a consequence it is impossible to prove the statement you have formulated.
If you want to be able to discriminate between the two constructors, you need to make foo an inductive Type rather than Prop. bar is then accepted as a valid proof.
Inductive foo : nat -> Type :=
| foo_1 : forall n, foo n
| foo_2 : forall n, foo n.
Lemma bar : foo_1 0 <> foo_2 0.
Proof. unfold not. intros H. discriminate H. Qed.
Short answer: you can't.
Let's take a simpler example, where we also fail to prove a similar thing:
Inductive baz : Prop :=
| baz1 : baz
| baz2 : baz.
Goal baz1 <> baz2.
intro H.
Fail discriminate H.
Abort.
The above fails with the following error message:
Error: Not a discriminable equality.
Now, let's try and find out where exactly discriminate fails.
First of all, let's take a detour and prove a very simple statement:
Goal false <> true.
intro prf; discriminate.
Qed.
We can also prove the above goal by providing its proof term directly, instead of building it using tactics:
Goal false <> true.
exact (fun prf : false = true =>
eq_ind false (fun e : bool => if e then False else True) I true prf).
Qed.
The above is a simplified version of what the discriminate tactic builds.
Let's replace false, true, and bool in the proof term with baz1, baz2, baz correspondingly and see what happens:
Goal baz1 <> baz2.
Fail exact (fun prf : baz1 = baz2 =>
eq_ind baz1 (fun e : baz => if e then False else True) I baz2 prf).
Abort.
The above fails with the following:
The command has indeed failed with message:
Incorrect elimination of e in the inductive type baz:
the return type has sort Type while it should be Prop.
Elimination of an inductive object of sort Prop
is not allowed on a predicate in sort Type
because proofs can be eliminated only to build proofs.
The reason of the error is this abstraction:
Fail Check (fun e : baz => if e then False else True).
The above produces the same error message.
And it's easy to see why. The abstraction's type is baz -> Prop and what's baz -> Prop's type?
Check baz -> Prop. (* baz -> Prop : Type *)
Maps from proofs of propositions to propositions live in Type, not in Prop! Otherwise it would cause universe inconsistency.
Our conclusion is that there is no way to prove the inequality, since to there is no way to break through Prop to do that -- you can't just use rewriting (baz1 = baz2) to build a proof of False.
Another argument (I see it has been already proposed by #gallais): if it was possible to use some clever trick and do the proof staying within Prop, then the proof irrelevance axiom would be inconsistent with Coq's logic:
Variable contra : baz1 <> baz2.
Axiom proof_irrelevance : forall (P:Prop) (p1 p2:P), p1 = p2.
Check contra (proof_irrelevance _ baz1 baz2). (* False *)
But, it's known to be consistent, see Coq's FAQ.
You might want to look at the Universes
chapter of CPDT, section "The Prop Universe" specifically.