Definition
Lean
Lean is a proof assistant and a functional programming language based on dependent type theory. Definitions construct terms; theorem proofs construct terms whose types are propositions. Its kernel checks that these terms have the claimed types.
Terms and types
The basic relation is t : T: the term t has type T. A type determines what terms can be used where; it is not merely a label attached to a variable.
x : Msays thatxis an element of the typeM.M : Type usays thatMis itself a type in a universe at levelu.P : Propsays thatPis a proposition.h : Psays thathis a proof of that proposition.
Types can depend on terms: in τ : L.Sentence, the type of τ depends on the particular language L. The dot here names an existing library declaration; it is not a built-in operation for mathematical sentences.
Functions and parameters
A term f : A → B has a function type. Applying it to a : A, written f a, produces a term of type B. The expression fun x => t constructs a function with input x and body t.
A dependent function has type (x : A) → B x: its result type depends on its input. Lean also writes this as ∀ (x : A), B x.
Multiple inputs are handled by currying: A → B → C means A → (B → C), and f a b means (f a) b.
The binder controls how an argument is supplied, not whether the function has that argument:
| Binder | Argument handling |
|---|---|
[[Knowledge/Explicit Parameter (Lean)|(x : T)]] | The caller normally supplies it. |
[[Knowledge/Implicit Parameter (Lean)|{x : T}]] | Lean tries to infer it from type constraints. |
[[Knowledge/Instance-Implicit Parameter (Lean)|[x : T]]] | Lean searches for an available instance of T. |
The declaration of the function determines which convention its callers encounter. Instance search is explained below.
Definitions and proofs
def name : T := t names a term t of type T. Parameters written before the colon become inputs to that definition.
theorem name : P := h names a proof h of proposition P. Merely defining a proposition with def name : Prop := P does not prove it.
The propositions-as-types correspondence explains why functions also express reasoning:
- A proof of
P → Qtakes a proof ofPand produces a proof ofQ. - A proof of
∀ x : A, P xtakes an arbitraryx : Aand produces a proof ofP x. - Given
h : P → Qandhp : P, function applicationh hpis a proof ofQ.
Thus implication and universal quantification use function types; they do not require an unrelated proof language.
Inductive types and structures
An inductive declaration defines a type through its constructors: the permitted ways to build its terms. Lean generates principles for defining functions and proving statements by considering those constructors. Recursive constructors give rise to induction hypotheses.
A structure is a record type: its constructor groups named fields into one value, and projections retrieve the fields. Fields can contain values, functions or proofs. This packages related information; it does not make that information automatically discoverable.
In mathlib, L.Structure M packages interpretations of language symbols on an already supplied domain M. The name belongs to the library; the record mechanism belongs to Lean.
Classes and instance search
A typeclass is a type registered for instance synthesis. The class declaration defines a structure and registers it for this mechanism.
An instance is a term made available to that search. The instance command registers one; [i : C] introduces one as a parameter; letI : C := value supplies one locally.
When a function requires an argument [i : C], Lean searches for an available term of type C and passes it. The function then uses that term just like any other argument. Merely having an ordinary parameter (i : C) does not register it for instance search.
This is why [I : L.Structure M] lets M ⊨ τ find the interpretation I: the library’s satisfaction machinery requests an instance of that type. M is still the domain, not the whole mathematical structure.
Elaboration and the kernel
Elaboration turns the source you write into expressions with names resolved and omitted information supplied:
Notation, inferred arguments and instance search make source expressions shorter. They do not remove the underlying arguments or allow ill-typed expressions past the kernel.
Kernel acceptance is relative to the axioms used. sorry introduces an admitted proof through sorryAx; a file accepted with sorry does not establish the unfinished claim. #print axioms theoremName reports a theorem’s axiom dependencies.
Tactics construct proof terms
A by block uses tactics to construct a term of the required type. A goal consists of a target type and a local context containing available variables and hypotheses.
- exact supplies a term of the target type.
- apply uses a function or theorem whose conclusion matches the target and creates goals for missing premises.
- have introduces an intermediate result; obtain extracts information from a hypothesis.
- rw, unfold and simp transform expressions using equalities or definitions.
- funext reduces function equality to pointwise equality.
- induction uses the type’s induction principle; generalising induction controls what the induction hypothesis quantifies over.
Tactics are proof-building tools, not additional unchecked inference rules. Their completed proof terms are checked by the kernel.
Reading unfamiliar Lean
Start from the declaration rather than guessing from the notation:
- Go to definition to find what a name or notation refers to.
- Use
#check nameto inspect its type and parameters. - Use
#print nameto inspect its declaration, including its body when available. - Read
(x : T),{x : T}and[x : T]to identify how its arguments are supplied. - If printed notation hides the mechanism,
set_option pp.all true in #check nameexposes more of the underlying expression.
Separate Lean’s language mechanisms—functions, inductive types, records and instances—from library declarations built using them, such as Language, L.Structure and satisfaction.