Consistent style across Mathlib’s hundreds of thousands of lines of code is essential for readability, searchability, and long-term maintainability. Mathlib enforces its style conventions through a combination of automated linters and human review. This page documents every rule a contributor needs to follow before a PR can be merged.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/leanprover-community/mathlib4/llms.txt
Use this file to discover all available pages before exploring further.
File Structure Overview
Every Mathlib.lean file must follow a strict top-to-bottom structure:
- Copyright header — machine-readable license block
- Import statements — one per line, in alphabetical order within each group
- Module docstring — a
/-! ... -/block describing the file’s mathematical content - Top-level declarations — definitions, lemmas, and theorems
File Header
Every Mathlib file must begin with a copyright and license block in this exact format:- The year should be the year of the initial commit.
- Multiple authors are listed comma-separated on the
Authors:line. - The header is enforced by the
linter.style.headerlinter, which is always enabled — even in files imported beforeMathlib.Init.
Module Docstring
Immediately after the imports, every file must include a module docstring using the/-! ... -/ comment syntax. This is distinct from a regular -- comment or a doc_string attached to a declaration — it documents the file as a whole.
# heading should match the module’s mathematical topic, not its file path. The ## Main definitions and ## Main results sections are conventional but optional for very small files. Implementation notes and references are encouraged whenever non-obvious choices were made.
Line Length
The hard maximum line length in Mathlib is 100 characters. Lines exceeding this limit are reported as errors by the style linter.The 100-character rule applies to all lines — including comments, docstrings, string literals, and code. There are no exceptions.
fix_long_lines.py script in the scripts/ directory can help automatically wrap some categories of long lines, but manual adjustment is usually required.
File Length
Files should not exceed 1500 lines. Thelinter.style.longFile linter (configured with .ofNat 1500 in lakefile.lean) will emit a warning when this threshold is crossed. If your file is approaching this limit, consider splitting it into logically coherent sub-modules.
Indentation
Mathlib uses 2-space indentation throughout. Tabs are never used.by block is indented 2 spaces relative to the by keyword’s line. Nested tactic blocks (e.g., induction cases) add another 2 spaces per level.
Unicode Symbols
Lean 4 and Mathlib make extensive use of Unicode. Contributors are expected to use the standard Unicode symbols rather than ASCII alternatives in all new code.| Symbol | Input sequence | Meaning |
|---|---|---|
∀ | \forall | For all |
∃ | \exists | There exists |
→ | \to or \r | Implies / function type |
↦ | \mapsto | Maps to (in fun) |
∧ | \and | Logical and |
∨ | \or | Logical or |
¬ | \not or \n | Negation |
≤ | \le | Less than or equal |
≥ | \ge | Greater than or equal |
∈ | \in | Set membership |
∉ | \notin | Not a member |
⊆ | \sub | Subset |
⊂ | \ssub | Strict subset |
ℕ | \N | Natural numbers |
ℤ | \Z | Integers |
ℚ | \Q | Rationals |
ℝ | \R | Reals |
ℂ | \C | Complex numbers |
Proof Style
Tactic proofs
Preferby tactic blocks for most proofs. Tactic proofs are more readable for human reviewers and easier to maintain as Lean evolves:
Term-mode proofs
Term-mode proofs are acceptable — and encouraged — when they are concise and the proof term is obviously correct:omega, simp, ring, and linarith
Use automation freely, but with judgment:
omegafor linear arithmetic overℕandℤ.ringfor equalities in commutative (semi)rings.linarithfor linear arithmetic over ordered fields.simpfor simplification with the simp set — but prefersimp [lemma_name]over baresimpin theorems that are not themselves@[simp]lemmas, so the proof is more robust.
decide
decide is useful for decidable propositions over finite types, but avoid it for propositions with large search spaces.
Docstrings
Every public definition, theorem, structure, and typeclass instance should have a docstring immediately preceding it, written as a/-- ... -/ comment:
section and namespace
Use namespace to group related declarations and to avoid name repetition:
section when you need to introduce local variable declarations or open statements that should not persist beyond the section:
variable Declarations
Use variable to avoid repeating type hypotheses across multiple related lemmas:
variable are only included in a theorem’s signature when they actually appear in that theorem’s statement or proof — Lean inserts them automatically.
@[simp] Lemmas
The @[simp] attribute marks a lemma as a rewrite rule for Lean’s simp tactic. Apply it carefully:
Good simp lemmas
Lemmas that simplify a term to a more normal form:
List.length_nil, Nat.zero_add, if_true. The right-hand side should be structurally simpler than the left.Bad simp lemmas
Lemmas that could create simp loops, lemmas whose RHS is not simpler, or lemmas that are only useful in very specific contexts. Prefer explicit rewriting with
rw in those cases.Avoiding sorry
Never submit a PR containing sorry. The sorry tactic admits any goal without proof and will make CI pass falsely. Use it only as a local placeholder during development, and remove all instances before opening a PR.
If you need to submit partial work, open a draft PR and explain which parts are incomplete.
adaptation_note
When a piece of code needs to change because of an upstream Lean or Batteries update, use the #adaptation_note command to explain the situation:
#adaptation_note is a Lean 4 command defined in Mathlib.Tactic.AdaptationNote. It works in command, tactic, and term mode, and must be followed by a /-- doc comment -/. This helps future contributors understand why the code looks the way it does and makes it easier to clean up after the upstream issue is resolved.
assert_not_exists
The assert_not_exists command checks that a given declaration is not yet imported, which is useful for verifying import independence:
Full example of a well-formatted Mathlib file
Full example of a well-formatted Mathlib file