Connectives, equivalences, natural deduction, and proofs in Lean
Christiano Braga · Mestrado em Sistemas e Computação · IME
Based on How To Prove It with Lean (HTPIwL), chapter 1.
Software controls aircraft, medical devices, financial systems, and communication networks. Errors cost money and lives.
Testing examines finitely many executions of a program that admits infinitely many.
Program testing can be used to show the presence of bugs, but never to show their absence!
E. W. Dijkstra, Notes on Structured Programming, EWD249, 1970.
State a property of a program as a mathematical proposition.
Prove that every execution satisfies it; the proof covers all inputs at once.
A proof assistant checks every step against the rules of a formal logic and helps build the proof interactively.
In current use: Lean, Rocq (formerly Coq), Isabelle/HOL, Agda. Landmarks: the seL4 microkernel and the CompCert C compiler.
Language models write a growing share of code. Plausible is not the same as correct; the failure mode is hallucination.
A machine-checked proof is verified independently of how the code was produced, so wrong code cannot pass.
The burden of correctness moves from reading the code to writing the right specification.
A proposition is a declarative sentence that is either true or false. In Lean the type Prop classifies them.
Connectives build compound propositions: ¬P, P ∧ Q, P ∨ Q, P → Q, P ↔ Q.
They are truth-functional, so the value of a compound proposition depends only on the values of its parts.
An implication whose antecedent is false is true, whatever its consequent says, because an implication claims nothing about those cases.
Disjunction is inclusive, so P ∨ Q also holds when P and Q hold together.
A valuation assigns a truth value to each variable. A tautology is true under every valuation.
A ≡ B when A ↔ B is a tautology, that is, the two agree under every valuation.
Name | Equivalence |
|---|---|
De Morgan | ¬(P ∧ Q) ≡ ¬P ∨ ¬Q |
De Morgan | ¬(P ∨ Q) ≡ ¬P ∧ ¬Q |
Double negation | ¬¬P ≡ P |
Contrapositive | P → Q ≡ ¬Q → ¬P |
Material implication | P → Q ≡ ¬P ∨ Q |
The implication is called material because its truth depends only on the truth values of P and Q, not on any connection of meaning between them (Russell, 1903).
Second De Morgan law: the columns for ¬(P ∨ Q) and ¬P ∧ ¬Q agree on all four valuations.
P | Q | P ∨ Q | ¬(P ∨ Q) | ¬P ∧ ¬Q |
|---|---|---|---|---|
T | T | T | F | F |
T | F | T | F | F |
F | T | T | F | F |
F | F | F | T | T |
Truth tables decide any propositional question, but grow exponentially in the number of variables and do not extend to the quantifiers of Lecture 2. A calculus derives instead of computing.
A calculus fixes axioms and inference rules. A derivation applies the rules, no valuation appears in it, and a machine can check it.
It is sound when every theorem is a tautology and complete when every tautology is a theorem. Post proved both for the propositional calculus in 1921.
An axiomatic calculus has many axioms and one rule. Łukasiewicz and Tarski need three schemes over → and ¬, with modus ponens.
A → (B → A) (A → (B → C)) → ((A → B) → (A → C)) (¬A → ¬B) → (B → A)
Resolution keeps one rule on clauses, which machine provers search with (Robinson, 1965).
Natural deduction has no axioms and two rules per connective, one to introduce it and one to eliminate it. This course uses it.
Each rule has premises above a line and a conclusion below, applied one step at a time (Gentzen, 1935).
Introduction rules prove a connective; elimination rules use it. Some rules discharge an assumption, marked [P].
[P]
⋮
Q P → Q P
─────── →I ───────────── →E
P → Q Q
P Q P ∧ Q P ∧ Q ─────── ∧I ─────── ∧E₁ ─────── ∧E₂ P ∧ Q P Q
P Q [P] [Q]
─────── ∨I₁ ─────── ∨I₂ P ∨ Q ⋮ ⋮
P ∨ Q P ∨ Q R R
────────────────────────── ∨E
R
The constant ⊥ is absurdity, and ¬P abbreviates P → ⊥.
[P]
⋮
⊥ P ¬P ⊥
─────── ¬I ───────── ¬E ───── ⊥E
¬P ⊥ C
The rules above are constructive. Classical logic adds one further rule, equivalently RAA or excluded middle.
[¬P]
⋮
⊥
───────── RAA ─────────── EM
P P ∨ ¬P
A declaration names a statement and gives its proof. The keyword comes first, then the name, then the hypotheses in parentheses, then the statement after the colon, then the proof after :=.
Term proof
theorem and_swap (P Q : Prop) (h : P ∧ Q) : Q ∧ P :=
⟨h.right, h.left⟩
fun h => e builds a function, and f a applies one.
⟨a, b⟩ is the anonymous constructor; h.left and h.right take a conjunction apart.
Tactic proof
example (P Q : Prop) (h : P ∧ Q) :
Q ∧ P := P:PropQ:Proph:P ∧ Q⊢ Q ∧ P
All goals completed! 🐙
by enters tactic mode, · focuses one goal, and sorry marks a missing proof.
#check prints the type of a term, and -- starts a comment.
The logical symbols are unicode, typed with a backslash abbreviation: \to for →, \and for ∧, \or for ∨, \not for ¬, \iff for ↔, \bot for ⊥, \langle and \rangle for ⟨ ⟩, and \. for ·.
A proof of a proposition is a term whose type is that proposition; an assumption is a variable of that type. Each rule builds or takes apart a term.
Rule | Lean term | Example |
|---|---|---|
assumption | a hypothesis name |
|
→I |
|
|
→E | application |
|
∧I |
|
|
∧E₁, ∧E₂ |
|
|
∨I₁, ∨I₂ |
|
|
∨E |
|
|
¬I |
|
|
¬E |
application into |
|
⊥E |
|
|
1. P ∧ Q → Q ∧ P
[P ∧ Q] [P ∧ Q]
─────────∧E₂ ─────────∧E₁
Q P
────────────────────── ∧I
Q ∧ P
────────────────────────── →I
P ∧ Q → Q ∧ P
example (P Q : Prop) : P ∧ Q → Q ∧ P :=
fun h => ⟨h.right, h.left⟩
fun h => is the →I discharging P ∧ Q
h.right and h.left are ∧E₂ and ∧E₁
⟨_, _⟩ is the ∧I
2. P → P ∨ Q
[P]
─────── ∨I₁
P ∨ Q
─────────── →I
P → P ∨ Q
example (P Q : Prop) : P → P ∨ Q :=
fun h => Or.inl h
fun h => is the →I discharging P
Or.inl is ∨I₁, choosing the left disjunct
P → ¬¬P in detail
¬A is A → False. Twice over: ¬¬P is (P → False) → False, and the statement is P → ((P → False) → False).
The parentheses are needed. Since → associates to the right, P → P → P → False is a different proposition, and a false one.
The derivation and the goal at each step
[¬P] [P]
──────────── ¬E
⊥
──────── ¬I
¬¬P
───────────── →I
P → ¬¬P
⊢ P → ¬¬P hP : P ⊢ ¬¬P hP : P, hnP : ¬P ⊢ False
One function per arrow
example (P : Prop) : P → ¬¬P :=
fun (hP : P) =>
fun (hnP : ¬P) => hnP hP
the first fun is the →I that discharges P
the second is the ¬I that discharges ¬P; its parameter has type ¬P, not P
hnP hP is the ¬E: a negative hypothesis is a function into False, and applying it to hP gives ⊥
A tactic transforms the proof state, the goal together with the hypotheses in scope, one step at a time.
by enters tactic mode, and the sequence elaborates to a proof term, so a tactic proof and a term proof yield the same object.
exact e closes the goal when the type of e is the goal, and have h : A := e is the forward step, which adds h : A to the context without touching the goal.
apply f is the backward step. It applies f to open arguments, unifies the conclusion of the type of f with the goal, and leaves every unsolved premise as a new goal. With goal Q and f : P → Q, the goal becomes P, and the term under construction is hPQ ?p, which exact hP completes into hPQ hP.
example (P Q : Prop) (hPQ : P → Q)
(hP : P) : Q := P:PropQ:ProphPQ:P → QhP:P⊢ Q
P:PropQ:ProphPQ:P → QhP:P⊢ P
All goals completed! 🐙
⊢ Q apply hPQ ⊢ P exact hP
Connective | Introduce | Eliminate |
|---|---|---|
→ |
|
|
∧ |
|
|
∨ |
|
|
¬ |
|
|
⊥ | (none) |
|
example (P Q : Prop) : P ∨ Q → Q ∨ P := P:PropQ:Prop⊢ P ∨ Q → Q ∨ P
P:PropQ:Proph:P ∨ Q⊢ Q ∨ P
cases h with
P:PropQ:ProphP:P⊢ Q ∨ P All goals completed! 🐙
P:PropQ:ProphQ:Q⊢ Q ∨ P All goals completed! 🐙
example (P : Prop) : ¬¬P → P := P:Prop⊢ ¬¬P → P
P:Proph:¬¬P⊢ P
P:Proph:¬¬P⊢ ¬P → False
P:Proph:¬¬PhnP:¬P⊢ False
All goals completed! 🐙
Introduction tactics build the goal; elimination tactics use a hypothesis. Classical reasoning adds Classical.byContradiction and Classical.em, needed for double negation elimination.
A tactic proof plays out like a board game. Each element of the game names a precise part of the proof.
Board game | Tactic proof |
|---|---|
The board | the proof state, the goal together with the hypotheses in scope |
Your pieces | the hypotheses you may use |
A move |
a tactic ( |
Splitting the board | a tactic that opens several goals; each must be won |
Two directions |
|
The rulebook | the introduction and elimination rules of natural deduction |
Winning | every goal closed, and Lean's kernel checks the final proof term |
Derivation
[P ∧ Q]
────────── ∧E₁
P
──────────── →I
P ∧ Q → P
Term mode
example (P Q : Prop) : P ∧ Q → P :=
fun h => h.left
fun h => is the →I discharging P ∧ Q
h.left is ∧E₁, the left projection
Tactic mode
example (P Q : Prop) : P ∧ Q → P := P:PropQ:Prop⊢ P ∧ Q → P
P:PropQ:Proph:P ∧ Q⊢ P
All goals completed! 🐙
intro h is the →I discharging P ∧ Q
exact h.left closes the goal by ∧E₁
Derivation
[⊥]
────── ⊥E
P
──────── →I
⊥ → P
Term mode
example (P : Prop) : False → P :=
fun h => False.elim h
fun h => is the →I discharging ⊥
False.elim h is ⊥E, giving any P
Tactic mode
example (P : Prop) : False → P := P:Prop⊢ False → P
P:Proph:False⊢ P
All goals completed! 🐙
intro h is the →I discharging ⊥
exact False.elim h closes the goal by ⊥E
Derivation
[(P→Q)∧P] [(P→Q)∧P]
───────────── ∧E₁ ───────────── ∧E₂
P → Q P
────────────────────────────── →E
Q
──────────────────────────────── →I
(P → Q) ∧ P → Q
Term mode
example (P Q : Prop) : (P → Q) ∧ P → Q :=
fun h => h.left h.right
fun h => is the →I discharging (P → Q) ∧ P
h.left and h.right are ∧E₁ and ∧E₂
the application h.left h.right is →E, modus ponens
Tactic mode
example (P Q : Prop) : (P → Q) ∧ P → Q := P:PropQ:Prop⊢ (P → Q) ∧ P → Q
P:PropQ:Proph:(P → Q) ∧ P⊢ Q
P:PropQ:Proph:(P → Q) ∧ P⊢ P
All goals completed! 🐙
intro h is the →I discharging (P → Q) ∧ P
apply h.left reduces the goal to P by →E
exact h.right supplies P by ∧E₂
Derivation
[P] [Q]
[P ∨ Q] ─────── ∨I₂ ─────── ∨I₁
Q ∨ P Q ∨ P
───────────────────────────────────── ∨E
Q ∨ P
────────────────────── →I
P ∨ Q → Q ∨ P
Term mode
example (P Q : Prop) : P ∨ Q → Q ∨ P :=
fun h => h.elim
(fun hP => Or.inr hP)
(fun hQ => Or.inl hQ)
fun h => is the →I discharging P ∨ Q
h.elim is ∨E, one branch per disjunct
Or.inr and Or.inl are ∨I, swapping the disjuncts
Tactic mode
example (P Q : Prop) : P ∨ Q → Q ∨ P := P:PropQ:Prop⊢ P ∨ Q → Q ∨ P
P:PropQ:Proph:P ∨ Q⊢ Q ∨ P
cases h with
P:PropQ:ProphP:P⊢ Q ∨ P All goals completed! 🐙
P:PropQ:ProphQ:Q⊢ Q ∨ P All goals completed! 🐙
intro h is the →I discharging P ∨ Q
cases h is ∨E, splitting into the two disjuncts
each branch closes with Or.inr / Or.inl (∨I)
Derivation
[¬P] [¬¬P]
────────────── ¬E
⊥
────────── RAA
P
─────────────── →I
¬¬P → P
Term mode
example (P : Prop) : ¬¬P → P :=
fun h => Classical.byContradiction (fun hnP => h hnP)
fun h => is the →I discharging ¬¬P
Classical.byContradiction is the classical rule (RAA)
fun hnP => h hnP derives ⊥ from ¬P and ¬¬P by ¬E
Tactic mode
example (P : Prop) : ¬¬P → P := P:Prop⊢ ¬¬P → P
P:Proph:¬¬P⊢ P
P:Proph:¬¬P⊢ ¬P → False
P:Proph:¬¬PhnP:¬P⊢ False
All goals completed! 🐙
intro h is the →I discharging ¬¬P
apply Classical.byContradiction invokes RAA
intro hnP then exact h hnP derive ⊥ by ¬E
A proposition is classified by Prop; the connectives ¬, ∧, ∨, →, ↔ build compound propositions.
Truth tables decide propositional questions but grow exponentially; natural deduction applies rules one step at a time and generalizes.
Each connective has introduction and elimination rules; some rules discharge assumptions.
A proof is a term whose type is the proposition (Curry-Howard): fun h => e for →I, application for →E, ⟨_, _⟩ for ∧I, Or.inl/Or.inr for ∨I.
Tactics transform the proof state: intro, exact, apply, cases, constructor, have; by elaborates them to the same proof term.
Classical reasoning adds Classical.byContradiction and Classical.em, needed for ¬¬P → P and one De Morgan law.
Exercises: see the lecture notes.