6.Β Advanced Topics
Once you can finish small proofs by hand, the next skill is working efficiently with a library the size of Mathlib. This part collects the techniques that experienced users rely on every day:
-
Automation: high-powered tactics (
simp,aesop,polyrith,decide,omega, ...) that close routine goals so you can focus on the interesting steps. -
Navigating Mathlib: how to find the right lemma -- by name, by type signature (
exact?,apply?, Loogle, Moogle), or by browsing the source. -
Common pitfalls: typeclass diamonds,
Nat-vs-Intcoercions, theβ€-vs-β₯convention, definitional unfolding, and other things that catch first-time users.
6.1.Β Proof Automation
In this chapter, we discuss the many automation tactics available in Lean and Mathlib. Choosing the right tactic for the right situation is one of the most important skills in proof engineering. When used well, automation can turn a 20-line manual proof into a single line. When used poorly, it can cause slow compilation and obscure proof scripts.
6.1.1.Β The simp tactic
The simp tactic is the workhorse of Lean automation. It applies a set of rewriting rules (called simp lemmas) repeatedly until no more rules apply. A lemma is marked as a simp lemma by adding the @[simp] attribute to it. Mathlib contains thousands of such lemmas.
Basic usage:
example (n : β) : n + 0 = n := n:ββ’ n + 0 = n All goals completed! π
example (s : Set β) : s β© Set.univ = s := s:Set ββ’ s β© Set.univ = s All goals completed! π
example (n : β) : n β ({n} : Set β) := n:ββ’ n β {n} All goals completed! π
The variant simp only [...] restricts simp to use only the listed lemmas. This is preferred in Mathlib contributions because it makes proofs more robust against changes in the simp set.
example (n : β) : 0 + n + 0 = n := n:ββ’ 0 + n + 0 = n All goals completed! π
The variant simp_all applies simp to all hypotheses and the goal simultaneously. This is more powerful than calling simp at * because simplifications in one hypothesis can unlock simplifications elsewhere.
example (n m : β) (h : n + 0 = m) : m = n := n:βm:βh:n + 0 = mβ’ m = n All goals completed! π
Performance warning: The global simp call (without only) searches through all simp lemmas, which can be slow. In library code, prefer simp only. In interactive exploration, plain simp is fine. You can use simp? to find out which lemmas simp uses, and then replace simp by simp only [...].
6.1.2.Β The aesop tactic
The aesop tactic (Automated Extensible Search for Obvious Proofs) performs a best-first tree search using a configurable set of rules. It can apply lemmas, split goals, use simp, and more. It is particularly good for goals involving set membership, basic logic, and algebraic structures.
example (P Q : Prop) (hP : P) (hQ : Q) : P β§ Q := P:PropQ:ProphP:PhQ:Qβ’ P β§ Q All goals completed! π
example (x : β) (s t : Set β) (hs : x β s) : x β s βͺ t := x:βs:Set βt:Set βhs:x β sβ’ x β s βͺ t All goals completed! π
You can register your own lemmas as aesop rules using the @[aesop] attribute:
@[aesop safe apply] -- always try applying this lemma @[aesop unsafe 50% apply] -- try with 50% priority
6.1.3.Β The omega tactic
The omega tactic decides linear arithmetic over β and β€. It handles equalities, inequalities, divisibility, and modular arithmetic involving linear expressions. It cannot handle multiplication of variables (that is nonlinear).
example (n : β) (h : n β₯ 3) : n β₯ 1 := n:βh:n β₯ 3β’ n β₯ 1 All goals completed! π
example (a b : β€) (h1 : a + b β€ 10) (h2 : a β₯ 3) : b β€ 7 := a:β€b:β€h1:a + b β€ 10h2:a β₯ 3β’ b β€ 7 All goals completed! π
example (n : β) : n % 2 = 0 β¨ n % 2 = 1 := n:ββ’ n % 2 = 0 β¨ n % 2 = 1 All goals completed! π
Note that omega works on β and β€ but not on β or β. For those, use linarith.
6.1.4.Β The decide tactic
The decide tactic works on decidable propositions -- propositions for which there is a computable decision procedure. This includes Boolean expressions, finite quantifiers, and computations on finite types.
example : 2 + 3 = 5 := β’ 2 + 3 = 5 All goals completed! π
example : Β¬ (3 β£ 7) := β’ Β¬3 β£ 7 All goals completed! π
example : Nat.Prime 17 := β’ Nat.Prime 17 All goals completed! π
Warning: decide runs a computation at kernel level. For large numbers, it can be extremely slow or time out. For example, Nat.Prime 104729 will take very long. In such cases, use norm_num instead.
6.1.5.Β The norm_num tactic
The norm_num tactic normalizes numerical expressions. It can prove equalities and inequalities involving concrete numbers, and it has extensions for primality, divisibility, and more.
example : (3 : β) * 7 = 21 := β’ 3 * 7 = 21 All goals completed! π
example : (2 : β) / 3 + 1 / 6 = 5 / 6 := β’ 2 / 3 + 1 / 6 = 5 / 6 All goals completed! π
example : Nat.Prime 104729 := β’ Nat.Prime 104729 All goals completed! π
example : Β¬ Nat.Prime 4 := β’ Β¬Nat.Prime 4 All goals completed! π
Unlike decide, norm_num uses verified computation and is much more efficient for numerical goals. It also works in types like β and β where decide does not apply.
6.1.6.Β The ring tactic
The ring tactic proves equalities in commutative (semi)rings. It works with β, β€, β, β, polynomial rings, and any type that has a CommRing or CommSemiring instance. It does not use hypotheses -- it only uses the ring axioms.
example (x y : β) : (x + y)^2 = x^2 + 2*x*y + y^2 := x:βy:ββ’ (x + y) ^ 2 = x ^ 2 + 2 * x * y + y ^ 2 All goals completed! π
example (a b c : β€) : (a - b) * (a + b) = a^2 - b^2 := a:β€b:β€c:β€β’ (a - b) * (a + b) = a ^ 2 - b ^ 2 All goals completed! π
6.1.7.Β polyrith and linear_combination
The linear_combination tactic proves an equality goal by providing a linear (or polynomial) combination of hypotheses. You supply the combination, and the tactic verifies it (usually via ring).
example (x y : β) (h1 : x + y = 3) (h2 : x - y = 1) : x = 2 := x:βy:βh1:x + y = 3h2:x - y = 1β’ x = 2
All goals completed! π
The polyrith tactic tries to find such a combination automatically by calling an external oracle. It then suggests a linear_combination call. This requires network access and may not always succeed.
6.1.8.Β The linarith tactic
The linarith tactic proves linear arithmetic goals over ordered fields and rings. Unlike omega, it works over β and β, but it only handles linear expressions (no multiplication of variables).
example (x y : β) (h1 : x β€ 3) (h2 : y β€ 5) : x + y β€ 8 := x:βy:βh1:x β€ 3h2:y β€ 5β’ x + y β€ 8 All goals completed! π
example (Ξ΅ : β) (hΞ΅ : Ξ΅ > 0) : Ξ΅ / 2 > 0 := Ξ΅:βhΞ΅:Ξ΅ > 0β’ Ξ΅ / 2 > 0 All goals completed! π
For nonlinear goals, you can sometimes help linarith by providing auxiliary facts:
example (x : β) (hx : x β₯ 0) : x^2 + x β₯ 0 := x:βhx:x β₯ 0β’ x ^ 2 + x β₯ 0
All goals completed! π
The variant nlinarith can sometimes handle nonlinear goals by trying to multiply hypotheses together.
6.1.9.Β The positivity tactic
The positivity tactic proves goals of the form 0 β€ e or 0 < e. It understands sums, products, powers, absolute values, norms, and more.
example (x : β) : 0 β€ x^2 + 1 := x:ββ’ 0 β€ x ^ 2 + 1 All goals completed! π
example (x y : β) (hx : 0 < x) (hy : 0 < y) : 0 < x * y + x := x:βy:βhx:0 < xhy:0 < yβ’ 0 < x * y + x All goals completed! π
6.1.10.Β The gcongr tactic
The gcongr (generalized congruence) tactic proves inequalities by matching the structure of both sides and reducing to component-wise inequalities. It is very useful for goals like "if a β€ b and c β€ d, then a + c β€ b + d".
example (a b c d : β) (hab : a β€ b) (hcd : c β€ d) : a + c β€ b + d := a:βb:βc:βd:βhab:a β€ bhcd:c β€ dβ’ a + c β€ b + d
All goals completed! π
6.1.11.Β The field_simp tactic
The field_simp tactic clears denominators and simplifies expressions involving division in a field. It is typically followed by ring to finish the proof.
example (x : β) (hx : x β 0) : x / x = 1 := x:βhx:x β 0β’ x / x = 1 All goals completed! π
example (a b : β) (ha : a β 0) (hb : b β 0) :
1 / a + 1 / b = (a + b) / (a * b) := a:βb:βha:a β 0hb:b β 0β’ 1 / a + 1 / b = (a + b) / (a * b)
a:βb:βha:a β 0hb:b β 0β’ b + a = a + b
All goals completed! π
Note that field_simp needs hypotheses that denominators are nonzero. It will look for these in the context.
6.1.12.Β Choosing the right tactic
Here is a rough guide for choosing the right automation tactic:
Goal type | Tactic |
|---|---|
Numerical computation ( |
|
Linear arithmetic over |
|
Linear arithmetic over |
|
Ring equality ( |
|
Nonlinear inequality |
|
Clearing denominators |
|
Simplification with known lemmas |
|
Set membership, basic logic |
|
Monotonicity / congruence |
|
Decidable finite computation |
|
Using hypotheses as linear combination |
|
When in doubt, try simp, then aesop, then more specialized tactics. Use simp? to discover which simp lemmas apply, and then switch to simp only for a more maintainable proof.
6.2.Β Navigating Mathlib
Mathlib is the community-maintained mathematical library for Lean 4. It contains hundreds of thousands of lines of formalized mathematics, covering algebra, analysis, topology, measure theory, number theory, combinatorics, and more. Learning to navigate Mathlib effectively is essential for writing proofs that build on existing results rather than reinventing the wheel.
6.2.1.Β How Mathlib is organized
The Mathlib source code lives in the Mathlib/ directory and is organized by mathematical area. Here are some of the main subdirectories:
-
Mathlib.Algebra: groups, rings, fields, modules, linear algebra -
Mathlib.Analysis: real analysis, normed spaces, calculus, measure theory -
Mathlib.Topology: topological spaces, continuous maps, filters -
Mathlib.Order: partial orders, lattices, well-founded relations -
Mathlib.Data: concrete data types (Nat,Int,Rat,Real,Fin,List,Set, ...) -
Mathlib.Logic: basic logic, classical reasoning, choice -
Mathlib.SetTheory: ordinals, cardinals -
Mathlib.NumberTheory: primes, arithmetic functions, quadratic reciprocity -
Mathlib.MeasureTheory: measures, integration, probability -
Mathlib.CategoryTheory: categories, functors, natural transformations -
Mathlib.Combinatorics: graph theory, combinatorial identities -
Mathlib.Tactic: custom tactics (ring,norm_num,positivity, ...)
Within each directory, files are further organized by specificity. For example, Mathlib.Topology.Basic contains the definition of topological spaces, while Mathlib.Topology.MetricSpace.Basic specializes to metric spaces.
6.2.2.Β Searching for results: exact?, apply?, rw?
Lean provides several tactics that search Mathlib for applicable lemmas. These are invaluable when you know what you need but do not know its name.
exact? searches for a single lemma (possibly with arguments) that closes the current goal. It will print a suggestion that you can copy into your proof.
example : (0 : β) < 1 := β’ 0 < 1 All goals completed! π
-- This might suggest: exact zero_lt_one
apply? searches for a lemma whose conclusion matches the goal, possibly leaving subgoals.
example : Continuous (fun x : β β¦ x ^ 2 + 1) := β’ Continuous fun x => x ^ 2 + 1
All goals completed! π
rw? searches for a lemma that can rewrite part of the goal.
These tactics can be slow because they search through a large library. Use them during exploration, but replace them with the concrete result they find for your final proof.
6.2.3.Β Using #check, #print, and #search
The commands #check and #print are fundamental for exploring the library interactively.
#check shows the type of a term or lemma:
#check Nat.add_comm -- Nat.add_comm : β (n m : β), n + m = m + n
#check Set.mem_union -- shows the type of the union membership lemma
#print shows the full definition of a term, including its proof:
#print Nat.add_comm -- shows the actual proof term
#print Set -- shows the definition of Set
If you are in VS Code, you can also use F12 (Go to Definition) to jump to the source code of any definition or lemma. This is one of the fastest ways to understand how something is defined.
6.2.4.Β External search tools: Moogle and Loogle
When you cannot guess the name of a lemma, external search tools can help.
Loogle (loogle.lean-lang.org) lets you search Mathlib by type signature. For example:
-
Searching for
List.mapshows all lemmas involvingList.map -
Searching for
_ + _ = _ + _finds commutativity and related lemmas -
Searching for
Continuous, Real.expfinds lemmas about continuity ofexp
Moogle (moogle.ai) uses natural language search powered by AI. You can type queries like:
-
"continuous function on compact set is bounded"
-
"sum of convergent series"
-
"every finite group is a quotient of a free group"
Both tools link directly to the Mathlib documentation and source code.
6.2.5.Β The Mathlib documentation website
The official Mathlib documentation is available at the Mathlib docs site. It provides:
-
A searchable index of all declarations
-
Rendered source code with clickable cross-references
-
Type signatures and docstrings
When browsing the docs, pay attention to the namespace. For example, Set.union_comm lives in the Set namespace, so you can use it as Set.union_comm or open Set and use union_comm.
6.2.6.Β Naming conventions in Mathlib
Mathlib follows systematic naming conventions that make lemma names predictable once you know the pattern. The general rule is:
The conclusion is described first, then hypotheses are listed after _of_.
For example:
-
continuous_add: continuity of addition (goal:Continuous ...) -
isOpen_of_isOpen_inter: a set is open given that some intersection is open -
le_of_lt:a β€ bfollows froma < b
Common abbreviations:
Full name | Abbreviation |
|---|---|
addition |
|
multiplication |
|
subtraction |
|
division |
|
negation |
|
inverse |
|
commutativity |
|
associativity |
|
left |
|
right |
|
if and only if |
|
less than |
|
less or equal |
|
greater than |
|
Common patterns:
-
X_comm: commutativity (a * b = b * a) -
X_assoc: associativity ((a * b) * c = a * (b * c)) -
X_zero/zero_X: interaction with zero (a * 0 = 0) -
X_one/one_X: interaction with one (a * 1 = a) -
X_self: applied to itself (a - a = 0) -
X_left/X_right: left/right variants
Examples:
#check mul_comm -- a * b = b * a
#check add_assoc -- (a + b) + c = a + (b + c)
#check mul_zero -- a * 0 = 0
#check sub_self -- a - a = 0
#check le_of_lt -- a < b β a β€ b
#check lt_of_le_of_lt -- a β€ b β b < c β a < c
Knowing these conventions lets you guess lemma names. For instance, if you need a + b - b = a, try add_sub_cancel. If you need the reverse direction of some iff, try appending .mpr or .mp.
6.2.7.Β Reading Mathlib source code
When using a Mathlib lemma, it is often helpful to look at its source code to understand exactly what it says. Here are some tips:
-
Use
F12in VS Code to jump to the definition. -
Read the type signature carefully. Implicit arguments (in
{...}) are inferred by Lean. Instance arguments (in[...]) are resolved by typeclass search. -
Look at the namespace. If a lemma is in
namespace Foo, it can be used on terms of typeFoowith dot notation:h.fooinstead ofFoo.foo h. -
Check the imports. The file header tells you what dependencies the file has.
For example, consider:
#check Set.Finite.union
-- Set.Finite.union : Set.Finite s β Set.Finite t β Set.Finite (s βͺ t)
This tells you: if s and t are finite sets, then s βͺ t is finite. Because it is in the Set.Finite namespace, you can also write hs.union ht if hs : Set.Finite s and ht : Set.Finite t.
6.2.8.Β Understanding implicit arguments and typeclass resolution
Lean uses several kinds of argument brackets:
-
(x : Ξ±)β explicit: you must provide this argument -
{x : Ξ±}β implicit: Lean infers this from context -
[inst : SomeClass Ξ±]β instance: resolved by typeclass search -
β¦x : Ξ±β¦β strict implicit: like{}but slightly different unification behavior
When applying a lemma, you usually only provide explicit arguments. If Lean cannot infer an implicit argument, you can provide it with @:
#check @add_comm β _ 3 5 -- explicitly providing the type and the arguments
Typeclass resolution is the mechanism by which Lean automatically finds, for example, that β is a CommRing or that β has a LinearOrder. This works through a chain of instances registered with the instance command.
6.2.9.Β Managing dependencies with lake
Lean projects use lake (Lean's build system and package manager) to manage dependencies. If you need Mathlib in your project, your lakefile.lean will contain something like:
require mathlib from git "https://github.com/leanprover-community/mathlib4" @ "main"
Useful lake commands:
-
lake buildβ build the project -
lake updateβ update dependencies -
lake exe cache getβ download precompiled Mathlib.oleanfiles (saves hours of compilation)
Always run lake exe cache get after updating Mathlib to avoid recompiling the entire library.
6.2.10.Β Common patterns in Mathlib proofs
Here are some patterns you will see frequently in Mathlib and should adopt:
Pattern 1: Dot notation for structure projections and namespace lemmas.
example (h : P β§ Q) : P := h.1
Pattern 2: Anonymous constructor notation.
example (hP : P) (hQ : Q) : P β§ Q := β¨hP, hQβ©
Pattern 3: calc blocks for chains of equalities or inequalities.
example (a b c : β) (h1 : a β€ b) (h2 : b β€ c) : a β€ c :=
calc a β€ b := h1
_ β€ c := h2
Pattern 4: suffices to restructure a proof by stating an intermediate goal.
example (n : β) (h : n > 0) : n * n > 0 := n:βh:n > 0β’ n * n > 0
suffices n β₯ 1 n:βh:n > 0this:n β₯ 1 := ?m.18β’ n * n > 0 All goals completed! π
All goals completed! π
Pattern 5: have for local forward reasoning, obtain for destructuring.
example (h : β n : β, n > 5 β§ n < 10) : β n : β, n > 3 := h:β n > 5, n < 10β’ β n, n > 3
n:βhn:n > 5rightβ:n < 10β’ β n, n > 3
exact β¨n, n:βhn:n > 5rightβ:n < 10β’ n > 3 All goals completed! πβ©
6.3.Β Common Pitfalls
Even experienced Lean users regularly run into frustrating situations where a proof that "should" work simply does not. This section catalogs the most common pitfalls and gives practical advice for diagnosing and fixing them.
6.3.1.Β Universe issues
Every type in Lean lives in a universe. The hierarchy is:
-
Prop(also calledSort 0) β the universe of propositions -
Type 0(also calledType) β the universe of "ordinary" types likeβ,β -
Type 1,Type 2, ... β higher universes
Most of the time, you do not need to think about universes. Problems arise when you write definitions that are universe-polymorphic and Lean cannot unify the universe levels.
Symptom: An error message mentioning universe level or Sort u_1.
Fix: Add explicit universe annotations. For example:
universe u v
variable {Ξ± : Type u} {Ξ² : Type v}
In practice, if you are working with concrete types (β, β, etc.) and Mathlib structures, universe issues are rare. They mostly appear when defining very general abstractions.
6.3.2.Β Coercion headaches
In mathematics, we freely treat a natural number as an integer, a rational as a real, and so on. In Lean, these are different types, and moving between them requires coercions, written β (or Nat.cast, Int.cast, etc.).
Lean inserts coercions automatically in many cases, but sometimes the coercion chain gets confused, especially when mixing several numeric types.
The standard coercion chain:
β β β€ β β β β β β
Common problem: Your goal looks right, but it involves a mismatch between, say, (n : β) and (βn : β€), and tactics like ring or linarith fail because the types do not match.
Tactics for coercion issues:
-
push_castpushes coercions inward: it rewritesβ(a + b)toβa + βb,β(a * b)toβa * βb, etc. -
pull_castpulls coercions outward (the reverse direction). -
norm_castnormalizes cast expressions and can close goals involving casts. -
exact_mod_castandapply_mod_castare versions ofexactandapplythat handle casts automatically.
example (n m : β) (h : (n : β€) + m = 10) : (β(n + m) : β€) = 10 := n:βm:βh:βn + βm = 10β’ β(n + m) = 10 n:βm:βh:βn + βm = 10β’ βn + βm = 10; All goals completed! π
example (n : β) (h : n < 5) : (n : β€) < 5 := n:βh:n < 5β’ βn < 5 All goals completed! π
Tip: When working with mixed types, try to state everything in the "largest" type from the start. For example, if you are working with both β and β, cast to β as early as possible and use push_cast and norm_cast to manage the casts.
6.3.3.Β Definitional equality surprises
In Lean, some equalities hold by definition (definitional equality), while others require a proof (propositional equality). The distinction can be surprising.
When rfl works: rfl closes a goal a = b when a and b are definitionally equal. For example:
example : 2 + 3 = 5 := β’ 2 + 3 = 5 All goals completed! π -- Lean computes this
example (n : β) : n + 0 = n := n:ββ’ n + 0 = n All goals completed! π -- by definition of Nat.add
When rfl fails:
-- This does NOT work: -- example (n : β) : 0 + n = n := by rfl -- Because Nat.add is defined by recursion on the first argument, -- 0 + n is not definitionally equal to n.
The change tactic lets you replace the goal with a definitionally equal one:
example (n : β) : Nat.succ n = n + 1 := n:ββ’ n.succ = n + 1
n:ββ’ n + 1 = n + 1
All goals completed! π
The show tactic does the same thing but is more readable when used at the start of a proof:
example (n : β) : Nat.succ n = n + 1 := n:ββ’ n.succ = n + 1
n:ββ’ n + 1 = n + 1
All goals completed! π
Tip: If a tactic unexpectedly fails, try using change to rewrite the goal into an equivalent form. Sometimes the goal looks right in the Infoview but contains hidden definitional mismatches.
6.3.4.Β Typeclass diamonds
A typeclass diamond occurs when there are two different paths to derive the same typeclass instance for a type, and these paths produce instances that are not definitionally equal.
For example, suppose β€ gets a Monoid instance both directly and through CommRing β Ring β Monoid. If these produce different Monoid structures, Lean may complain about type mismatches.
Symptom: An error like "type mismatch" where the types look identical in the Infoview, or rfl fails on something that "obviously" should work.
How Mathlib avoids diamonds: Mathlib carefully designs its typeclass hierarchy so that diamonds are definitionally equal. This is one reason why the hierarchy is so carefully structured with extends and forgetful inheritance. When defining your own instances, follow the same patterns.
Practical advice: If you encounter a diamond, check whether you have defined a typeclass instance that conflicts with one from Mathlib. The fix is usually to ensure your instance agrees definitionally with the existing one, or to remove it and use the one from Mathlib.
6.3.5.Β Simp loop issues
A simp loop occurs when two simp lemmas rewrite back and forth indefinitely. For example, if both a = b and b = a were simp lemmas, simp would loop forever.
Symptom: simp takes very long or times out.
How to diagnose: Use set_option trace.Meta.Tactic.simp true in before your tactic call to see which lemmas simp is applying. If you see the same lemma applied repeatedly, you have a loop.
-- Diagnose simp behavior: -- set_option trace.Meta.Tactic.simp true in -- example : ... := by simp
Prevention: When marking a lemma @[simp], ensure it rewrites toward a "simpler" or "canonical" form. The left-hand side should be more complex than the right-hand side. Never add both a = b and b = a as simp lemmas.
6.3.6.Β Dependent type complications
Lean's type system is very expressive, but dependent types can cause headaches when types that "should" be equal are not definitionally equal.
The classic problem: You have h : a = b and want to rewrite in a type that depends on a. But after rewriting, the type of some term changes, and Lean complains about a type mismatch.
Tools for dealing with this:
-
subst h: ifh : a = bandais a variable, this substitutesbforaeverywhere. -
congr/congr 1: tries to reduce an equality goal to equalities of the components. -
convert e: likeexact ebut allows the types to differ, generating subgoals for the mismatches. -
Eq.mprandcast: explicitly transport a term along an equality of types (rarely needed in tactic mode).
HEq (Heterogeneous equality): Sometimes two terms have different types but you want to say they are "equal." This is what HEq provides. It should be avoided when possible, as it is harder to work with than Eq.
Practical advice: If you get stuck with dependent type issues, try:
-
Rewrite earlier so that types match before you build dependent terms.
-
Use
convertinstead ofexactwhen the goal is almost right. -
Use
simporcongrto reduce the problem.
6.3.7.Β Timeout issues
Lean has a computational budget measured in heartbeats. When a tactic or elaboration exceeds this budget, you get a timeout error.
Common causes:
-
simpwith too many lemmas or a simp loop -
decideon a large computation -
Typeclass search going through too many instances
-
Deeply nested term elaboration
How to increase the budget (for debugging only):
set_option maxHeartbeats 400000 in example : ... := by sorry
The default is 200000. Increasing it is a band-aid, not a solution. Find the root cause.
How to diagnose:
-- See what simp is doing: set_option trace.Meta.Tactic.simp true in -- See typeclass search: set_option trace.Meta.synthInstance true in -- Profile a tactic: set_option profiler true in
6.3.8.Β Practical debugging tips
Here is a collection of debugging techniques that every Lean user should know.
1. Use the Infoview. Always keep the Lean Infoview panel open in VS Code. Place your cursor after each tactic to see the current goal state. This is the single most important debugging tool.
2. Type annotations. When Lean gives a confusing error, add explicit type annotations to help it (and yourself) understand what is going on:
-- Instead of: -- let x := f a -- Write: -- let x : β := f a
3. #check everything. If you are not sure what type a lemma expects, #check it:
#check mul_le_mul_of_nonneg_left
-- β {Ξ± : Type u_1} [inst : OrderedSemiring Ξ±] {a b c : Ξ±},
-- a β€ b β 0 β€ c β c * a β€ c * b
4. Binary search for errors. If a long proof breaks, comment out the bottom half and see if the top half is OK. Then narrow down. You can also replace a tactic with sorry to skip it temporarily.
5. trace options. Lean has many trace options for understanding what is happening:
set_option trace.Meta.Tactic.simp true -- trace simp
set_option trace.Meta.synthInstance true -- trace typeclass search
set_option trace.Elab.definition true -- trace definition elaboration
6. Minimize the example. If you are stuck, try to create a minimal example that reproduces the issue. Remove all unnecessary hypotheses, imports, and context. This often reveals the problem.
7. Check Zulip. The Lean Zulip chat is an active community where you can ask questions. Search the archive first -- your question has likely been asked before.
8. Use conv for surgical rewriting. When rw rewrites in the wrong place, use conv to target a specific subexpression:
example (a b : β) : a + b + a = a + a + b := a:βb:ββ’ a + b + a = a + a + b
conv_lhs => a:βb:β| b + a + a
All goals completed! π
9. The ? suffix. Many tactics have a ? variant that suggests a more explicit call:
-
simp?suggestssimp only [...] -
exact?suggests the exact term to use -
apply?suggests which lemma to apply -
rw?suggests which lemma to rewrite with
Use the ? variant during exploration, then copy the suggestion into your final proof for robustness and speed.