Week 1: Intro
- Author
Nate Foster.
- License
No redistribution allowed.
Propositional logic
Let's explore more proofs. This one states a tautology: “if A, then A”. We encode if
as an implication, written ->
, and we quantify over all possible propositions A
by writing forall (A: Prop)
:
forall A : Prop, A -> Aforall A : Prop, A -> A
Goals in Coq have two parts: the context (or hypotheses) and the conclusion. A horizontal bar separates them; on paper, we would use a turnstile (⊢
) instead.
The intros
tactic changes ⊢ A -> A
to A ⊢ A
, which visually means moving A above the bar.
A: Prop
H: AA
Notice that the newly created hypothesis was given a name automatically (H).
The apply
tactic is the modus ponens rule: it takes a term or a hypothesis of the form A -> B
, and changes the conclusion from B
to A
. In the simplest case, where the hypothesis is simply A
, and the conclusion is A
as well, apply
solves the goal entirely:
apply H. Qed.
Proofs that rely on automatically generated names can be brittle. Thankfully, the intros
tactic can name the hypotheses it creates. For example:
forall A B : Prop, A /\ B -> B /\ Aforall A B : Prop, A /\ B -> B /\ AA, B: Prop
HAB: A /\ BB /\ A
The destruct
tactic performs case analysis on its argument, and gives names to each part. Here, we use it to name each side of HAB
:
A, B: Prop
HA: A
HB: BB /\ A
This proof can be completed by explicitly construction a proof of B /\ A
from the two terms HB
and HA
:
apply (conj HB HA). Qed.
But what is conj
?
conj
is the name of the single constructor of the type and
! So our proof is really just unpacking a conj
(that's the first destruct
), then repacking it (the apply
). Print shows us as much:
If /\ is in fact a type, then are True
and False
also types? Indeed yes! True
has a single constructor, called I
, and False
, thankfully, has none:
What happens if we call destruct
on a term of type True
? No much, since it carries no information and no subterms:
True -> TrueTrue -> TrueHT: TrueTrueapply I. Qed.True
What about False
? Having a term of type False
is impossible, since False
has no constructors. Consequently, a case analysis yields… 0 cases, and a proof.
forall A : Prop, False -> Aforall A : Prop, False -> Adestruct HF. Qed.A: Prop
HF: FalseA
Some Coq tactics are rather smart. For example, destruct
, when applied to a term of type X -> Y
, first creates a new goal for X
, then performs destruct
on the specialization of the original term. Here is this behavior in action:
forall A : Prop, ~ True -> Aforall A : Prop, ~ True -> AA: Prop
HnotT: ~ TrueAapply I. Qed.A: PropTrue
What happened? destruct
peeked into the definition of not
:
Bools and props
Here is a theorem, and its proof, on propositions:
forall A : Prop, ~ (A /\ ~ A)forall A : Prop, ~ (A /\ ~ A)forall A : Prop, A /\ (A -> False) -> FalseA: Prop
H: A /\ (A -> False)FalseA: Prop
H1: A
H2: A -> FalseFalseapply H1. Qed.A: Prop
H1: A
H2: A -> FalseA
And here is now a suspiciously similar-looking theorem, on Booleans:
b: boolnegb (b && negb b) = trueb: boolnegb (b && negb b) = truenegb (true && negb true) = truenegb (false && negb false) = truereflexivity. Qed.negb (false && negb false) = true
Booleans are not the same, in Coq, as propositions: booleans are a simple two-valued type, whereas Prop
is a kind (a type of types). One way to relate the two is to use equality: true = true
, for example, is a proposition:
So is true = false
:
Definition bool_bogus : Prop := true = false.
Thankfully, while true = true
is provable, true = false
is not:
bool_bogusbool_bogus(* No way to make progress. *) Abort.true = false
On the other hand, the negation of bool_bogus
is provable:
~ bool_bogus~ bool_bogustrue = false -> Falsediscriminate Heq. Qed.Heq: true = falseFalse
Let's explore this bool
/Prop
connection further. Theorems T4
and T5
were very similar. Shouldn't we be able to reuse T4
to prove T5
?
While it's not strictly necessary, the following axiom will help:
Axiom prop_ext : forall (P Q : Prop), (P <-> Q) -> P = Q.
We start with an injection from bool
to Prop
: bool_to_prop b
is provable when b
is equal to true
.
Definition bool_to_prop (b: bool) : Prop := b = true.
We can use bool_to_prop
to relate Prop
and bool
operators:
neg
:b: boolbool_to_prop (negb b) = (~ bool_to_prop b)b: boolbool_to_prop (negb b) = (~ bool_to_prop b)b: bool(negb b = true) = (b <> true)b: boolnegb b = true <-> b <> trueb: boolnegb b = true -> b <> trueb: boolb <> true -> negb b = trueb: boolnegb b = true -> b <> truenegb true = true -> true <> truenegb false = true -> false <> truediscriminate.negb false = true -> false <> trueb: boolb <> true -> negb b = truetrue <> true -> negb true = truefalse <> true -> negb false = truetrue <> true -> negb true = trueH: true <> truenegb true = truereflexivity.true = truereflexivity. Qed.false <> true -> negb false = trueand
:b1, b2: boolbool_to_prop (b1 && b2) = (bool_to_prop b1 /\ bool_to_prop b2)b1, b2: boolbool_to_prop (b1 && b2) = (bool_to_prop b1 /\ bool_to_prop b2)b1, b2: boolbool_to_prop (b1 && b2) <-> bool_to_prop b1 /\ bool_to_prop b2b1, b2: boolbool_to_prop (b1 && b2) -> bool_to_prop b1 /\ bool_to_prop b2b1, b2: boolbool_to_prop b1 /\ bool_to_prop b2 -> bool_to_prop (b1 && b2)b1, b2: boolbool_to_prop (b1 && b2) -> bool_to_prop b1 /\ bool_to_prop b2bool_to_prop (true && true) -> bool_to_prop true /\ bool_to_prop truebool_to_prop (true && false) -> bool_to_prop true /\ bool_to_prop falsebool_to_prop (false && true) -> bool_to_prop false /\ bool_to_prop truebool_to_prop (false && false) -> bool_to_prop false /\ bool_to_prop falsebool_to_prop (true && true) -> bool_to_prop true /\ bool_to_prop trueH: bool_to_prop (true && true)bool_to_prop trueH: bool_to_prop (true && true)bool_to_prop truereflexivity.H: bool_to_prop (true && true)bool_to_prop truediscriminate.bool_to_prop (true && false) -> bool_to_prop true /\ bool_to_prop falsediscriminate.bool_to_prop (false && true) -> bool_to_prop false /\ bool_to_prop truediscriminate.bool_to_prop (false && false) -> bool_to_prop false /\ bool_to_prop falseb1, b2: boolbool_to_prop b1 /\ bool_to_prop b2 -> bool_to_prop (b1 && b2)bool_to_prop true /\ bool_to_prop true -> bool_to_prop (true && true)bool_to_prop true /\ bool_to_prop false -> bool_to_prop (true && false)bool_to_prop false /\ bool_to_prop true -> bool_to_prop (false && true)bool_to_prop false /\ bool_to_prop false -> bool_to_prop (false && false)reflexivity.bool_to_prop true /\ bool_to_prop true -> bool_to_prop (true && true)bool_to_prop true /\ bool_to_prop false -> bool_to_prop (true && false)discriminate.H1: bool_to_prop true
H2: bool_to_prop falsebool_to_prop (true && false)bool_to_prop false /\ bool_to_prop true -> bool_to_prop (false && true)discriminate.H1: bool_to_prop false
H2: bool_to_prop truebool_to_prop (false && true)bool_to_prop false /\ bool_to_prop false -> bool_to_prop (false && false)discriminate. Qed.H1, H2: bool_to_prop falsebool_to_prop (false && false)b: boolbool_to_prop (negb (b && negb b))b: boolbool_to_prop (negb (b && negb b))b: bool~ bool_to_prop (b && negb b)b: bool~ (bool_to_prop b /\ bool_to_prop (negb b))apply (T4 (bool_to_prop b)). Qed.b: bool~ (bool_to_prop b /\ ~ bool_to_prop b)