ExaktAI Workspace: Lean 101
A compact, live introduction to writing mathematics and checking proofs in the Workspace. This tutorial assumes mathematical familiarity but no Lean experience. Open a section, run its examples, then change something and observe the goals. The early sections introduce the language; the later ones build proofs and combine Lean with CAS exploration.
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 is the tutorial as it ships with the Workspace (Help > Tutorials > Lean 101), with the results of its own run. To run an example, change it and check it again, open it in the Workspace. ← Lean in the Workspace
How to use this live tutorial
import Mathlib: when a Lean document opens, the Workspace starts loading Mathlib in the background. Until it is loaded, an input is checked with Lean alone, at once; a result reached that way is marked “(core Lean)”. An input that needs Mathlib first shows Lean's answer without it, then is checked again with Mathlib as soon as it has loaded, which takes several seconds the first time in a session. From then on the document is checked with Mathlib, the inputs above included. A document that imports something itself keeps its own imports.sorry.1. Expressions, types, and evaluation
#eval evaluates an expression; #check reports its type. A type annotation (expression : Type) makes the intended number system explicit. Types affect the meaning of operations.#eval (2 + 3 * 4 : Nat)
14
#check (2 + 3 : Nat)
2 + 3 : Nat
Nat); Lean did not compute it.#eval (3 - 5 : Nat)
0
Int, ℤ).#eval (3 - 5 : Int)
-2
Rat, ℚ).#eval ((1 : Rat) / 3 + (1 : Rat) / 6)
(1 : Rat)/2
2. Definitions and reusable names
(n : Nat) as “n is a natural number”, the next : Nat as the result type, and := as “is defined to be”.def l101_double (n : Nat) : Nat := n + n
l101_double now names this function; it is applied by writing it before its argument.#eval l101_double 7
14
l101_double.#check l101_double
l101_double (n : Nat) : Nat
l101_ belong to this tutorial. Refer to a Lean definition or theorem by its name; a Workspace CAS result label is not a Lean proof term. When experimenting, edit an existing definition or choose a fresh name rather than declaring the same name twice.3. Your first theorem: statement, proof, goal
theorem name : statement := by starts a tactic proof. A tactic is an instruction that transforms or solves a goal. In Lean’s goal display, assumptions appear above ⊢; the statement still to prove appears after it.rfl proves an equality whose two sides compute to the same thing.theorem l101_add_zero (n : Nat) : n + 0 = n := by
rfl
theorem l101_add_zero (n : Nat) : n + 0 = n := byrfl
No goals
theorem l101_double_three : l101_double 3 = 6 := by
rfl
theorem l101_double_three : l101_double 3 = 6 := byrfl
No goals
4. Assumptions and implications
Prop is the type of propositions. A parameter (h : P) is evidence that P holds. exact h closes a goal when h proves exactly the required proposition.theorem l101_assumption (P : Prop) (h : P) : P := by
exact h
theorem l101_assumption (P : Prop) (h : P) : P := byexact h
No goals
intro h assumes P and names the assumption h; watch it move above ⊢.theorem l101_identity (P : Prop) : P → P := by
intro h
exact h
theorem l101_identity (P : Prop) : P → P := byintro h
exact h
No goals
:= by at the end of this theorem. Expect an unfinished proof with its initial goal. Restore the two proof lines. sorry is a placeholder for missing evidence, not a completed proof.5. Several goals: “and”, branches, and “or”
P ∧ Q requires both parts. constructor creates two goals. A bullet · opens a branch for one goal. Indentation matters. Given h : P ∧ Q, h.1 proves P and h.2 proves Q.theorem l101_swap_and (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
constructor
· exact h.2
· exact h.1
theorem l101_swap_and (P Q : Prop) (h : P ∧ Q) : Q ∧ P := byconstructor
· exact h.2
· exact h.1
No goals
left chooses to prove the left alternative.theorem l101_choose_left (P Q : Prop) (h : P) : P ∨ Q := by
left
exact h
theorem l101_choose_left (P Q : Prop) (h : P) : P ∨ Q := byleft
exact h
No goals
right would choose Q.6. Rewriting and simplification
rw [h] uses an equality to replace one side by the other. simp repeatedly applies known simplification rules. Supplying a definition inside its brackets lets it unfold that definition as part of simplification.theorem l101_rewrite (a b : Nat) (h : a = b) : a + 1 = b + 1 := by
rw [h]
theorem l101_rewrite (a b : Nat) (h : a = b) : a + 1 = b + 1 := byrw [h]
No goals
rw [h] both sides read b + 1.theorem l101_simplify (n : Nat) : l101_double n + 0 = n + n := by
simp [l101_double]
theorem l101_simplify (n : Nat) : l101_double n + 0 = n + n := bysimp [l101_double]
No goals
unfold l101_double followed by simp, on separate indented lines. Compare the intermediate goal with the one-step proof.7. Exact arithmetic and polynomial identities
norm_num proves concrete numerical facts; ring proves polynomial identities by normalization. These tactics construct evidence Lean checks.theorem l101_fraction : (1 / 3 : ℚ) + 1 / 6 = 1 / 2 := by
norm_num
theorem l101_fraction : (1 / 3 : ℚ) + 1 / 6 = 1 / 2 := bynorm_num
No goals
theorem l101_square (x : ℝ) : (x + 1)^2 = x^2 + 2*x + 1 := by
ring
theorem l101_square (x : ℝ) : (x + 1)^2 = x^2 + 2*x + 1 := byring
No goals
ring checks the algebra itself; no value of x was tried.theorem l101_difference_of_squares (x y : ℝ) :
(x - y) * (x + y) = x^2 - y^2 := by
ring
theorem l101_difference_of_squares (x y : ℝ) :
(x - y) * (x + y) = x^2 - y^2 := byring
No goals
8. Inequalities: choose the number system
omega handles many goals involving linear arithmetic over the integers. linarith combines linear equalities and inequalities, here over the reals. Keep the assumptions explicit.theorem l101_nat_bound (n : Nat) (h : n ≥ 3) : n + 2 ≥ 5 := by
omega
theorem l101_nat_bound (n : Nat) (h : n ≥ 3) : n + 2 ≥ 5 := byomega
No goals
theorem l101_real_bound (x : ℝ) (h : x ≥ 3) : 2*x + 1 ≥ 7 := by
linarith
theorem l101_real_bound (x : ℝ) (h : x ≥ 3) : 2*x + 1 ≥ 7 := bylinarith
No goals
9. Existence: supply a witness
∃ n : Nat, n > 10, give one suitable n and prove its property. refine ⟨11, ?_⟩ supplies 11 and leaves a goal where the question-mark placeholder sits. The next tactic must close that goal.theorem l101_witness : ∃ n : Nat, n > 10 := by
refine ⟨11, ?_⟩
norm_num
theorem l101_witness : ∃ n : Nat, n > 10 := byrefine ⟨11, ?_⟩
norm_num
No goals
10. A readable chain of equalities
calc lays out a chain. Each equality needs a justification. The underscore denotes the previous expression in the chain. A theorem proved earlier can be used by name, with its arguments.l101_square from section 7.theorem l101_square_shift (x : ℝ) : (x + 1)^2 - 1 = x^2 + 2*x := by
calc
(x + 1)^2 - 1 = (x^2 + 2*x + 1) - 1 := by rw [l101_square]
_ = x^2 + 2*x := by ring
theorem l101_square_shift (x : ℝ) : (x + 1)^2 - 1 = x^2 + 2*x := bycalc
(x + 1)^2 - 1 = (x^2 + 2*x + 1) - 1 := by rw [l101_square]
_ = x^2 + 2*x := by ringNo goals
11. Induction: prove all natural numbers at once
ih. In the step, congrArg Nat.succ ih adds 1 to both sides of ih, and simpa matches the result with the goal.theorem l101_zero_add (n : Nat) : 0 + n = n := by
induction n with
| zero => rfl
| succ n ih =>
simpa only [Nat.add_succ] using congrArg Nat.succ ih
theorem l101_zero_add (n : Nat) : 0 + n = n := byinduction n with
| zero => rfl
| succ n ih =>
simpa only [Nat.add_succ] using congrArg Nat.succ ihNo goals
12. Mini-project: explore with CAS, certify with Lean
factor(x**2 - 1)SymPytheorem l101_factor (x : ℝ) : x^2 - 1 = (x - 1) * (x + 1) := by
ring
theorem l101_factor (x : ℝ) : x^2 - 1 = (x - 1) * (x + 1) := byring
No goals
factor(x**2 - 1) and run it. Then recheck the generated ring step. This places exploration between proof steps; it does not make a CAS answer part of Lean’s trusted proof.Real.sqrt is Mathlib’s square root.sqrt(8)SymPy-- candidate from the sympy line: sqrt(8)
theorem cas_candidate_1 : Real.sqrt 8 = 2 * Real.sqrt 2 := by
rw [show (8 : ℝ) = 2 ^ 2 * 2 by norm_num, Real.sqrt_mul (by norm_num), Real.sqrt_sq (by norm_num)]
theorem cas_candidate_1 : Real.sqrt 8 = 2 * Real.sqrt 2 := byrw [show (8 : ℝ) = 2 ^ 2 * 2 by norm_num, Real.sqrt_mul (by norm_num), Real.sqrt_sq (by norm_num)]
No goals
h1 : x - 1 ≠ 0.cancel((x**2 - 1)/(x - 1))SymPy-- candidate from the sympy line: cancel((x**2 - 1)/(x - 1))
theorem cas_candidate_2 (x : ℝ) (h1 : x - 1 ≠ 0) : (x^2 - 1) / (x - 1) = x + 1 := by
field_simp
ring
theorem cas_candidate_2 (x : ℝ) (h1 : x - 1 ≠ 0) : (x^2 - 1) / (x - 1) = x + 1 := byfield_simp
ring
No goals
theorem l101_root_one : (1 : ℝ)^2 - 1 = 0 := by
rw [l101_factor]
norm_num
theorem l101_root_one : (1 : ℝ)^2 - 1 = 0 := byrw [l101_factor]
norm_num
No goals
l101_root_minus_one stating (-1 : ℝ)^2 - 1 = 0. Adapt the two proof steps above. Then try replacing 0 by 1: Lean should reject the proposed proof.13. Reading results and continuing
def, theorem, #check, #eval; then rfl, intro, exact, constructor, rw, simp, norm_num, ring, omega and linarith. Choose a tactic for the kind of goal you have, and inspect the new goal before continuing.