Lean 101: from a mathematical argument to a checked proof
For anyone who has met proofs, in mathematics, in physics or any other science, or simply for the pleasure of them: the arguments you already know, written so that Lean checks them.
This tutorial runs on your own Lean installation, with Mathlib. Tools ▸ Check CAS and AIs shows whether Lean is ready on this computer; Help ▸ How to ▸ Setting up Lean describes the installation.
This page reproduces exactly what the Workspace shows at Help > Tutorials > Lean 101, with the results of its own run. With the Workspace and Lean installed on your computer, every input in the Workspace runs live: an example can be changed and checked again, a proof can be taken in another direction, and each exercise can be tried before its solution is opened. ← Lean in the Workspace
What Lean brings
- States the claim.
In Lean. The claim is written as an exact proposition.
Lean words.theorem,lemma,example.
What Lean adds. Before the proof begins, the exact claim is fixed, including its objects, types, assumptions and quantifiers. - Shows goals.
In Lean. When Enter is pressed, Lean checks the input and shows the first goal, the claim itself.
Lean words.:= by.
What Lean adds. At every stage, the exact goals and the available hypotheses are shown. A step missing from the proof leaves its goal open, in view, and the proof is reported incomplete. - Proves.
In Lean. The proof is constructed step by step.
Lean words. Tactics such asintro,exact,constructor,rw,simp,ringandinduction.
What Lean adds. As the proof is written, every step must fit a current goal and becomes part of the complete proof. - Checks.
In Lean. Checks the completed proof against the exact claim.
Lean words. None: every Enter checks.#print axiomsshows what a checked result rests on.
What Lean adds. When no goals remain, Lean independently verifies the finished proof. Later proofs in the same document can cite it by name.
- Defines, when needed.
In Lean. A new object or function receives a name and an exact definition.
Lean words.def; later,structureandinductive.
What Lean adds. From its definition onward, the name keeps one exact meaning. Lean can replace the name with the definition in a computation or proof. - Computes, when needed.
In Lean. Lean evaluates a value or reduces an expression when a calculation is useful.
Lean words.#eval; in proofs,rflandnorm_num.
What Lean adds. A computation used inside a proof is part of the checked proof. A value Lean computes with#evalis displayed when the computation is successful.
What Lean computes
- With
#eval, Lean runs the computation and displays the value: exact integer and rational arithmetic of any size (), gcd, remainders, powers, integer factorization (Nat.primeFactorsList 360gives ), primality tests. A real number such as , or a polynomial as Mathlib defines it, has no value to display. - Inside a proof, tactics compute to build the proof:
rflanddecidesettle closed facts on and ();norm_numnumerical facts in any number system, primality included ( is prime);ringpolynomial identities of any degree, andfield_simpfractions with nonzero denominators;linarith,nlinarith,positivityandomegainequalities and linear arithmetic;simprewriting with Mathlib’s lemmas. - Solving equations, factoring polynomials, integrals, limits and series are the CAS’s part: the CAS finds, and Lean checks what it found (section 13).
Approaching Lean
What to expect
How to use this tutorial
- Every section ends with an Exercise, and its Solution, collapsed under it, is live like everything here: it runs, it reproduces its result on the Lean on this computer, and it can be edited and run again. The paper argument can come first.
- Enter checks the selected input. Shift+Enter adds a line inside it.
- An input at rest is shown typeset; a click opens its source, as typed, with the cursor where the click landed, and Enter typesets it again.
Natand are the same to Lean. - A backslash and a name, then a space or Tab, give a symbol, with the names VS Code uses:
\and,\or,\to,\forall,\exists,\ne,\ge,\R,\N,\.the bullet ·,\<\>the angle brackets ⟨ ⟩. The Math button in the toolbar opens the palette: in a Lean input it shows Tactics and Symbols, and its search accepts a name, a symbol or a\abbreviation. - Names introduced in an earlier input are available below it.
- The results shown were produced when the tutorial was made, and their badges say Saved check; Enter checks an input again, on the Lean on this computer.
- After a check, the steps of a proof appear below it, each with its goals. An edit in one of those steps changes the proof above at once, and its goals are outdated until the next Enter; after an edit to the proof itself, the steps below are read-only until it is checked again.
- The goal after each line is the most direct explanation of what that line did.
- A goal may carry a line
caseand a name: Lean’s name for that goal, from the step that made it (hafteruse;leftandrightafterconstructor;inlandinrafterrcaseson an “or”). - F1 with the cursor on a Lean word (
theorem,ring,linarith…) opens its help page: its forms, a description, and examples that run. On a name from Mathlib, such asNat.exists_infinite_primes, it shows the statement and documentation that the Lean on this computer has for it. Help ▸ Command help opens the same pages by search. - Mathlib needs no
import Mathlib: the Workspace starts loading it when a Lean document opens, and until it is loaded an input is checked with Lean alone. Tools ▸ Check CAS and AIs ▸ Lean shows the Lake project and toolchain in use, and chooses another project. - An input changed while exploring comes back with Undo.
1. State the claim, then read the goal
Example
theorem states the claim; := by opens its proof; rfl closes an equality that Lean can obtain directly by reduction.theorem fermat_two_mod_101 : 2^100 % 101 = 1 := by
rfl
theorem fermat_two_mod_101 : 2^100 % 101 = 1 := byrfl
rfl, Lean computes , a 31-digit number, and its remainder; no goal remains, and the Workspace reports Proof checked for this exact statement.Exercise
Solution
theorem fermat_three_mod_17 : 3^16 % 17 = 1 := by
rfl
theorem fermat_three_mod_17 : 3^16 % 17 = 1 := byrfl
2. Assume, then conclude
intro moves into the hypotheses, under a name.Example
intro names the assumption; obtain exposes its factor; use supplies the factor for the conclusion; rw and ring finish the equality.theorem divides_product (a b c : ℤ) : a ∣ b → a ∣ b*c := by
intro h
obtain ⟨k, hk⟩ := h
use k*c
rw [hk]
ring
theorem divides_product (a b c : ℤ) : a ∣ b → a ∣ b*c := byintro h
obtain ⟨k, hk⟩ := h
use k*crw [hk]
ring
intro h, obtain reveals an integer with . The witness proves that also divides .Exercise
Solution
theorem divides_sum (a b c : ℤ) : a ∣ b → a ∣ c → a ∣ (b+c) := by
intro hab hac
obtain ⟨k, hk⟩ := hab
obtain ⟨l, hl⟩ := hac
use k+l
rw [hk, hl]
ring
theorem divides_sum (a b c : ℤ) : a ∣ b → a ∣ c → a ∣ (b+c) := byintro hab hac
obtain ⟨k, hk⟩ := hab
obtain ⟨l, hl⟩ := hac
use k+lrw [hk, hl]
ring
3. Split a claim, or choose a side
Example
constructor creates the two goals; h.1 and h.2 are the two facts contained in the hypothesis; exact closes a goal with a fact that matches it; dvd_add and dvd_sub are Mathlib’s results for a sum and a difference (section 5 says more about citing results); left or right chooses a side of an “or”.theorem common_divisor_sum_difference (a b d : ℤ) (h : d ∣ a ∧ d ∣ b) :
d ∣ a + b ∧ d ∣ a - b := by
constructor
· exact dvd_add h.1 h.2
· exact dvd_sub h.1 h.2
theorem common_divisor_sum_difference (a b d : ℤ) (h : d ∣ a ∧ d ∣ b) :
d ∣ a + b ∧ d ∣ a - b := byconstructor
· exact dvd_add h.1 h.2
· exact dvd_sub h.1 h.2
constructor, Lean displays the two divisibility claims separately. Each closes with the corresponding Mathlib result applied to the two parts of h.Exercise
Solution
theorem square_is_four (x : ℝ) (h : x^2 = 4) : x = 2 ∨ x = -2 := by
have h' : (x - 2) * (x + 2) = 0 := by ring_nf; linarith
rcases mul_eq_zero.mp h' with h1 | h1
· left
linarith
· right
linarith
theorem square_is_four (x : ℝ) (h : x^2 = 4) : x = 2 ∨ x = -2 := byhave h' : (x - 2) * (x + 2) = 0 := by ring_nf; linarithrcases mul_eq_zero.mp h' with h1 | h1
· left
linarith
· right
linarith
have states and proves an intermediate fact, : ring_nf multiplies out the product, and linarith (section 8) finishes from h. mul_eq_zero is Mathlib’s result that a product is zero exactly when one of its factors is. rcases splits that “or” into its two cases; in each, left or right chooses the side of the conclusion, and linarith closes it.4. Replace equal things by equal things
Example
rw [h] replaces using h; rw [← h] replaces in the opposite direction; simp applies Mathlib’s simplification rules; Nat.gcd is the greatest common divisor of two natural numbers, as Mathlib defines it.theorem euclid_step (a b n r : ℕ) (h : a = n*b + r) :
Nat.gcd a b = Nat.gcd r b := by
rw [h]
simp
theorem euclid_step (a b n r : ℕ) (h : a = n*b + r) :
Nat.gcd a b = Nat.gcd r b := byrw [h]
simp
simp knows that adding a multiple of does not change a gcd with , and closes it.Exercise
Solution
theorem divides_remainder (a b n r k : ℤ) (h : a = n*b + r) (ha : k ∣ a) (hb : k ∣ b) :
k ∣ r := by
have hr : r = a - n*b := by rw [h]; ring
rw [hr]
exact dvd_sub ha (dvd_mul_of_dvd_right hb n)
theorem divides_remainder (a b n r k : ℤ) (h : a = n*b + r) (ha : k ∣ a) (hb : k ∣ b) :
k ∣ r := byhave hr : r = a - n*b := by rw [h]; ringrw [hr]
exact dvd_sub ha (dvd_mul_of_dvd_right hb n)
have states and proves the intermediate fact (section 3); rw [hr] replaces by it in the goal; exact closes the goal with dvd_sub applied to and to , the latter from dvd_mul_of_dvd_right, Mathlib’s result that a divisor of divides .5. Use a result already known
Example
exact supplies a theorem directly; apply uses a theorem and shows any conditions it still needs as new goals; exact? can suggest a matching result; Nat.Prime is primality of a natural number, as Mathlib defines it, written Nat.Prime p or p.Prime.theorem prime_above (n : ℕ) : ∃ p, n ≤ p ∧ Nat.Prime p := by
exact Nat.exists_infinite_primes n
theorem prime_above (n : ℕ) : ∃ p, n ≤ p ∧ Nat.Prime p := byexact Nat.exists_infinite_primes n
Nat.exists_infinite_primes matches the whole goal, so citing it completes the proof: Euclid’s theorem, as Mathlib has it.theorem euclid_lemma (p a b : ℕ) (hp : p.Prime) (h : p ∣ a * b) :
p ∣ a ∨ p ∣ b := by
apply (Nat.Prime.dvd_mul hp).mp
exact h
theorem euclid_lemma (p a b : ℕ) (hp : p.Prime) (h : p ∣ a * b) :
p ∣ a ∨ p ∣ b := byapply (Nat.Prime.dvd_mul hp).mp
exact h
Nat.Prime.dvd_mul. Applying its forward direction leaves as the only goal; the hypothesis closes it.Exercise
Solution
theorem consecutive_coprime (n : ℕ) : Nat.gcd n (n + 1) = 1 := by
simp
theorem consecutive_coprime (n : ℕ) : Nat.gcd n (n + 1) = 1 := bysimp
simp closes it with Mathlib’s result that consecutive natural numbers are coprime.6. Compute when computation settles the point
Example
#eval displays a computable value; norm_num proves exact numerical claims, unfolding the definitions named in its brackets; ring proves polynomial identities.#eval Nat.gcd 252 105
theorem gcd_value_252_105 : Nat.gcd 252 105 = 21 := by
norm_num [Nat.gcd]
theorem gcd_value_252_105 : Nat.gcd 252 105 = 21 := bynorm_num [Nat.gcd]
norm_num constructs a proof of the gcd equality, and Lean checks it.theorem brahmagupta_fibonacci (a b c d : ℝ) :
(a^2 + b^2) * (c^2 + d^2) =
(a*c - b*d)^2 + (a*d + b*c)^2 := by
ring
theorem brahmagupta_fibonacci (a b c d : ℝ) :
(a^2 + b^2) * (c^2 + d^2) =
(a*c - b*d)^2 + (a*d + b*c)^2 := byring
ring, no goal remains. The checked theorem holds for every real in one universal statement.Exercise
Solution
theorem sophie_germain_identity (a b : ℝ) :
a^4 + 4*b^4 =
(a^2 - 2*a*b + 2*b^2) * (a^2 + 2*a*b + 2*b^2) := by
ring
theorem sophie_germain_identity (a b : ℝ) :
a^4 + 4*b^4 =
(a^2 - 2*a*b + 2*b^2) * (a^2 + 2*a*b + 2*b^2) := byring
7. Respect the number system
Example
Nat, Int, Rat and ℝ name common number systems; #check reports an expression’s type.#check (3 - 5 : Nat)
3 - 5 : ℕ
Nat. Natural-number subtraction cannot produce a negative number.#eval (3 - 5 : Nat)
#eval (3 - 5 : Int)
Exercise
Solution
#eval (7 / 2 : Nat)
#eval ((7 : Rat) / 2)
8. Carry inequalities from the hypotheses
Example
constructor separates the two inequalities; linarith proves linear consequences over the reals; nlinarith also uses products and squares, given as hints; omega handles linear natural-number and integer arithmetic.theorem midpoint_between (a b : ℝ) (h : a ≤ b) :
a ≤ (a + b) / 2 ∧ (a + b) / 2 ≤ b := by
constructor
· linarith
· linarith
theorem midpoint_between (a b : ℝ) (h : a ≤ b) :
a ≤ (a + b) / 2 ∧ (a + b) / 2 ≤ b := byconstructor
· linarith
· linarith
constructor separates the lower and upper bounds. Each call to linarith uses the same assumption to close one goal.Exercise
Solution
theorem two_mul_le_sum_squares (a b : ℝ) : 2*a*b ≤ a^2 + b^2 := by
nlinarith [sq_nonneg (a - b)]
theorem two_mul_le_sum_squares (a b : ℝ) : 2*a*b ≤ a^2 + b^2 := bynlinarith [sq_nonneg (a - b)]
sq_nonneg (a - b) is that fact, from Mathlib; nlinarith expands it and combines it with the goal.9. Exhibit an object
Example
use supplies the witnesses; the remaining conditions become goals; norm_num can prove the numerical conditions.theorem exists_prime_above_hundred :
∃ p : Nat, p > 100 ∧ Nat.Prime p := by
use 101
norm_num
theorem exists_prime_above_hundred :
∃ p : Nat, p > 100 ∧ Nat.Prime p := byuse 101norm_num
norm_num proves both.Exercise
Solution
theorem pell_has_solution :
∃ x y : ℤ, y > 0 ∧ x^2 - 2*y^2 = 1 := by
use 3, 2
norm_num
theorem pell_has_solution :
∃ x y : ℤ, y > 0 ∧ x^2 - 2*y^2 = 1 := byuse 3, 2
norm_num
10. Write a chain of equalities
Example
calc starts the chain; each := by proves one link; _ repeats the preceding right-hand side.theorem gcd_by_euclid_252_105 : Nat.gcd 252 105 = 21 := by
calc
Nat.gcd 252 105 = Nat.gcd 105 42 := by norm_num [Nat.gcd]
_ = Nat.gcd 42 21 := by norm_num [Nat.gcd]
_ = 21 := by norm_num [Nat.gcd]
theorem gcd_by_euclid_252_105 : Nat.gcd 252 105 = 21 := bycalc
Nat.gcd 252 105 = Nat.gcd 105 42 := by norm_num [Nat.gcd]
_ = Nat.gcd 42 21 := by norm_num [Nat.gcd]
_ = 21 := by norm_num [Nat.gcd]calc block records the successive gcd pairs , and . Lean checks every equality in the chain.Exercise
calc chain to prove by the Euclidean algorithm.Solution
theorem gcd_by_euclid_240_46 : Nat.gcd 240 46 = 2 := by
calc
Nat.gcd 240 46 = Nat.gcd 46 10 := by norm_num [Nat.gcd]
_ = Nat.gcd 10 6 := by norm_num [Nat.gcd]
_ = Nat.gcd 6 4 := by norm_num [Nat.gcd]
_ = Nat.gcd 4 2 := by norm_num [Nat.gcd]
_ = 2 := by norm_num [Nat.gcd]
theorem gcd_by_euclid_240_46 : Nat.gcd 240 46 = 2 := bycalc
Nat.gcd 240 46 = Nat.gcd 46 10 := by norm_num [Nat.gcd]
_ = Nat.gcd 10 6 := by norm_num [Nat.gcd]
_ = Nat.gcd 6 4 := by norm_num [Nat.gcd]
_ = Nat.gcd 4 2 := by norm_num [Nat.gcd]
_ = 2 := by norm_num [Nat.gcd]11. Define something when the argument needs it
Example
def introduces the name; #eval computes a value; rfl can prove an equality obtained directly from the definition.def eulerPolynomial (n : Nat) : Nat := n^2 + n + 41
eulerPolynomial as a function from natural numbers to natural numbers. That name is now available below.List.range 40 is the list ; .all tests every element with the function written after fun n =>.#eval (List.range 40).all (fun n => Nat.Prime (eulerPolynomial n))
true
true: all forty are prime. #eval computes and displays; it does not prove, and the theorem below is what Lean checks.theorem euler_polynomial_40 : eulerPolynomial 40 = 41^2 := by
rfl
theorem euler_polynomial_40 : eulerPolynomial 40 = 41^2 := byrfl
Exercise
Solution
def eulerSeventeen (n : Nat) : Nat := n^2 + n + 17
theorem euler_seventeen_16 : eulerSeventeen 16 = 17^2 := by
rfl
theorem euler_seventeen_16 : eulerSeventeen 16 = 17^2 := byrfl
12. Prove a whole family by induction
Example
induction n with opens the two cases; ih is the result already available for ; Finset.range n is the set and .sum adds the function over it; Finset.sum_range_succ separates the last term of the finite sum; simp closes the case , where the sum is empty.theorem sum_first_odd (n : Nat) :
(Finset.range n).sum (fun k => 2*k + 1) = n^2 := by
induction n with
| zero => simp
| succ n ih =>
rw [Finset.sum_range_succ, ih]
ring
theorem sum_first_odd (n : Nat) :
(Finset.range n).sum (fun k => 2*k + 1) = n^2 := byn : ℕ ⊢ ∑ k ∈ Finset.range n, (2 * k + 1) = n ^ 2
induction n with
| zero => simp
| succ n ih =>
rw [Finset.sum_range_succ, ih]
ringih supplies the sum through ; the final term completes the next square.Exercise
Solution
theorem gauss_sum (n : Nat) :
2 * (Finset.range (n + 1)).sum (fun k => k) = n * (n + 1) := by
induction n with
| zero => simp
| succ n ih =>
rw [Finset.sum_range_succ]
rw [mul_add, ih]
ring
theorem gauss_sum (n : Nat) :
2 * (Finset.range (n + 1)).sum (fun k => k) = n * (n + 1) := byn : ℕ ⊢ 2 * ∑ k ∈ Finset.range (n + 1), k = n * (n + 1)
induction n with
| zero => simp
| succ n ih =>
rw [Finset.sum_range_succ]
rw [mul_add, ih]
ringmul_add is the distributive law, , from Mathlib; rewriting with it separates twice the sum through , the left side of ih, which then replaces it.13. Let a CAS explore; let Lean certify
Example
theorem states it in Lean; ring proves the identity.factor((m**2 - n**2)**2 + (2*m*n)**2)SymPytheorem pythagorean_parameterization (m n : ℝ) :
(m^2 - n^2)^2 + (2*m*n)^2 = (m^2 + n^2)^2 := by
ring
theorem pythagorean_parameterization (m n : ℝ) :
(m^2 - n^2)^2 + (2*m*n)^2 = (m^2 + n^2)^2 := byring
ring constructs its proof. The result now belongs to the exact universal statement shown above.cancel((x**2 - 1)/(x - 1))SymPyfield_simp clears the denominator, which that hypothesis allows; ring finishes.theorem cancel_with_condition (x : ℝ) (h1 : x - 1 ≠ 0) :
(x^2 - 1) / (x - 1) = x + 1 := by
field_simp
ring
theorem cancel_with_condition (x : ℝ) (h1 : x - 1 ≠ 0) :
(x^2 - 1) / (x - 1) = x + 1 := byfield_simp
ring
Exercise
Solution
theorem odd_leg (k : ℤ) : ∃ b c : ℤ, (2*k + 1)^2 + b^2 = c^2 := by
use 2*k^2 + 2*k, 2*k^2 + 2*k + 1
ring
theorem odd_leg (k : ℤ) : ∃ b c : ℤ, (2*k + 1)^2 + b^2 = c^2 := byuse 2*k^2 + 2*k, 2*k^2 + 2*k + 1
ring
use supplies them, and ring checks the identity for every .What “Proof checked” says
#print axioms displays the axioms used by a theorem.#print axioms pythagorean_parameterization
'pythagorean_parameterization' depends on axioms: [propext, Classical.choice, Quot.sound]
The vocabulary you now have
theorem opens a precise claim; goals display what remains; tactics build the proof; the kernel checks it.- Assume, expose and supply:
intro,obtain,exact,apply,have,rcases. - Split and choose:
constructor,left,right. - Transform:
rw,simp. - Inspect and calculate:
#check,#eval,rfl,norm_num,ring. - Arithmetic consequences:
omega,linarith,nlinarith. - Build mathematical forms:
use,calc,induction. - Introduce an object when needed:
def.