1.5. First Proofs in Lean
In Lean, we state a proposition and prove it in one declaration. The example keyword introduces an anonymous statement, and theorem introduces a named one. Hypotheses appear before the colon as named assumptions, and the proposition to prove, the goal, appears after it.
The simplest proof uses a hypothesis directly.
example (P : Prop) (h : P) : P := h
Here h names the assumption that P holds, and the proof is h itself. A proof of a proposition is a term whose type is that proposition. Lecture 3 develops this correspondence between propositions and types.W. A. Howard, The Formulae-as-Types Notion of Construction, in To H. B. Curry: Essays on Combinatory Logic, Lambda Calculus and Formalism, Academic Press, 1980.
Most proofs use tactics, commands that transform the goal step by step. The keyword by enters tactic mode. Each connective comes with rules to introduce it, proving a goal of that shape, and rules to eliminate it, using a hypothesis of that shape.
1.5.1. Implication
The tactic intro introduces an implication. To prove P → Q, assume P under a chosen name and prove Q.
example (P Q : Prop) (hQ : Q) : P → Q := P:PropQ:ProphQ:Q⊢ P → Q
P:PropQ:ProphQ:Q_hP:P⊢ Q
All goals completed! 🐙
The tactic exact closes the goal with a term that proves it. To use an implication, apply it to a proof of its antecedent. A hypothesis hPQ of type P → Q is a function from proofs of P to proofs of Q, so hPQ hP proves Q. This is the rule of modus ponens.
example (P Q : Prop) (hPQ : P → Q) (hP : P) : Q := hPQ hP
The tactic apply uses the same rule in the backward direction. Applying hPQ to the goal Q leaves P as the new goal.
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! 🐙
The tactic have reasons forward, adding a new hypothesis derived from the current ones, and show states the current goal explicitly. Both make proofs read like structured mathematical arguments.
example (P Q R : Prop) (hPQ : P → Q) (hQR : Q → R)
(hP : P) : R := P:PropQ:PropR:ProphPQ:P → QhQR:Q → RhP:P⊢ R
P:PropQ:PropR:ProphPQ:P → QhQR:Q → RhP:PhQ:Q⊢ R
P:PropQ:PropR:ProphPQ:P → QhQR:Q → RhP:PhQ:Q⊢ R
All goals completed! 🐙
1.5.2. Conjunction
To prove P ∧ Q, prove both parts. The tactic constructor splits the goal in two, and the bullet · delimits the proof of each.
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := P:PropQ:Proph:P ∧ Q⊢ Q ∧ P
P:PropQ:Proph:P ∧ Q⊢ QP:PropQ:Proph:P ∧ Q⊢ P
P:PropQ:Proph:P ∧ Q⊢ Q All goals completed! 🐙
P:PropQ:Proph:P ∧ Q⊢ P All goals completed! 🐙
To use a conjunction, project its parts with .left and .right. The anonymous constructor ⟨_, _⟩ builds the pair directly, giving a term-style proof.
example (P Q : Prop) (h : P ∧ Q) : Q ∧ P :=
⟨h.right, h.left⟩
1.5.3. Disjunction
To prove P ∨ Q, choose a side. Or.inl proves it from P, and Or.inr proves it from Q. To use a disjunction, reason by cases. The tactic cases produces one goal per disjunct.
example (P Q : Prop) (h : 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! 🐙
1.5.4. Negation
In Lean, ¬P is defined as P → False, where False is the proposition with no proof. A proof of ¬P is a function that turns any proof of P into a proof of False.
example (P : Prop) (hP : P) (hnP : ¬P) : False := hnP hP
Every tactic for implication therefore works for negation. The contrapositive direction below needs only intro and application.
theorem contrapositive (P Q : Prop) (hPQ : P → Q) :
¬Q → ¬P := P:PropQ:ProphPQ:P → Q⊢ ¬Q → ¬P
P:PropQ:ProphPQ:P → QhnQ:¬QhP:P⊢ False
All goals completed! 🐙
Introducing double negation is equally direct.
example (P : Prop) (hP : P) : ¬¬P := fun hnP => hnP hP
The second De Morgan law combines the rules seen so far. The tactic constructor also introduces a biconditional, splitting it into the two implications.
theorem deMorgan_or (P Q : Prop) : ¬(P ∨ Q) ↔ ¬P ∧ ¬Q := P:PropQ:Prop⊢ ¬(P ∨ Q) ↔ ¬P ∧ ¬Q
P:PropQ:Prop⊢ ¬(P ∨ Q) → ¬P ∧ ¬QP:PropQ:Prop⊢ ¬P ∧ ¬Q → ¬(P ∨ Q)
P:PropQ:Prop⊢ ¬(P ∨ Q) → ¬P ∧ ¬Q P:PropQ:Proph:¬(P ∨ Q)⊢ ¬P ∧ ¬Q
P:PropQ:Proph:¬(P ∨ Q)⊢ ¬PP:PropQ:Proph:¬(P ∨ Q)⊢ ¬Q
P:PropQ:Proph:¬(P ∨ Q)⊢ ¬P P:PropQ:Proph:¬(P ∨ Q)hP:P⊢ False
All goals completed! 🐙
P:PropQ:Proph:¬(P ∨ Q)⊢ ¬Q P:PropQ:Proph:¬(P ∨ Q)hQ:Q⊢ False
All goals completed! 🐙
P:PropQ:Prop⊢ ¬P ∧ ¬Q → ¬(P ∨ Q) P:PropQ:Proph:¬P ∧ ¬QhPQ:P ∨ Q⊢ False
cases hPQ with
P:PropQ:Proph:¬P ∧ ¬QhP:P⊢ False All goals completed! 🐙
P:PropQ:Proph:¬P ∧ ¬QhQ:Q⊢ False All goals completed! 🐙
1.5.5. Classical Reasoning
The rules used so far are constructive. Two principles of classical logic do not follow from them, the law of excluded middle and the elimination of double negation. Lean provides both in the Classical namespace.
#check Classical.em
Classical.byContradiction proves P from a proof that ¬P is impossible. With it, double negation elimination is one application away.
theorem not_not_elim (P : Prop) (h : ¬¬P) : P := P:Proph:¬¬P⊢ P
P:Proph:¬¬P⊢ ¬P → False
P:Proph:¬¬PhnP:¬P⊢ False
All goals completed! 🐙
The first De Morgan law requires classical reasoning. A case analysis on Classical.em P decides which disjunct to prove.
theorem deMorgan_and (P Q : Prop) : ¬(P ∧ Q) → ¬P ∨ ¬Q := P:PropQ:Prop⊢ ¬(P ∧ Q) → ¬P ∨ ¬Q
P:PropQ:Proph:¬(P ∧ Q)⊢ ¬P ∨ ¬Q
cases Classical.em P with
P:PropQ:Proph:¬(P ∧ Q)hP:P⊢ ¬P ∨ ¬Q All goals completed! 🐙
P:PropQ:Proph:¬(P ∧ Q)hnP:¬P⊢ ¬P ∨ ¬Q All goals completed! 🐙