
elan install stable. To start a project, follow the instructions of the official Lean manual.There is a standard library shipped with Lean. The file Init.Prelude is imported by default when you open a Lean file.
Other libraries can be used as dependencies in a Lean package:
Lean and all the libraries above are open-source (under an Apache 2.0 license).
The code is freely accessible on GitHub.
Kevin Buzzard is currently leading the formalization of Fermat's Last Theorem (2024-2029). The big picture for the proof can be visualized as follows thanks to Lean blueprint:

The colour code shows the advancement of the project towards completion.
The following tools are indispensable when writing Lean code:
(?a -> ?b) -> List ?a -> List ?b.order subgroup divides order group or dim V = dim (Ker u) + dim (Im u).The following web-based books are standard references in the Leanprover community:
lake init or lake new (try lake --help first). This can also be done directly from within VS Code, using the Lean 4 extension.Depending on whom you ask, a type may be seen as:
(3 : Nat), (-4 : Int), ([1, 2, 3] : List Nat), (Nat.mul : Nat → Nat → Nat).Every term has an associated type, which is part of its definition as a term. Since they do not have the same type, (3 : Nat) and (3 : Int) are different objects. Expressions such as x := x + 1 are not well-typed and do not make sense in a functional programming language.
In Lean, you can find out the type of an expression by using the #check command:
#check 42 -- 42 : Nat
#check Nat -- Nat : Type
#check Nat.mul -- Nat.mul : Nat → Nat → Nat
#check "42" -- "42" : String
#check 1 + 1 -- 1 + 1 : Nat
#check 1 + 1 = 2 -- 1 + 1 = 2 : Prop
#check 1 + 1 > 2 -- 1 + 1 > 2 : Prop
Note that a well-typed expression can be recognised as a proposition regardless of whether it is "mathematically correct". The expression 2 + 2 = 5, for instance, is well-typed / syntactically correct (an equality between two natural numbers). In contrast, a common expression such as 2 * (3 + 1) = 2 * 3 + 2 * 1 = 8 is not well-typed.
It is common sense not to mix quantities that are not related to one another. Here is an example of an ill-typed expression.
Image credits: MikeGogulski, CC BY-SA 3.0.
The architecture of an interactive theorem prover can be represented as follows.
Image credits: Assia Mahboubi.
f : X → Y are the basic objects of a language like Lean.def fact : Nat → Nat
| 0 => 1
| k + 1 => (k + 1) * fact k
#check fact -- fact : Nat → Nat
#check fact 5 -- fact 5 : Nat
#eval fact 5 -- 120
f : A → B → C, then, in the expression f a b, the term f a is a function from B to C, and it is applied to b.def sum : Nat → Nat → Nat := fun x y ↦ x + y
#check sum 3 -- sum 3 : Nat → Nat
#eval sum 3 5 -- 8
If we set instead def sum₁ : Nat → Nat → Nat := fun x ↦ (fun y ↦ x + y), then we get sum = sum₁, by reflexivity.
f : A → B → C → D → E, then for all a : A, b : B, c : C, d : D, we have f a b c d : E.f : A → (B → C) → D → E, which takes as arguments a term a : A, a function u : B → C and a term d : D, returning a term of type E.A → B → C → D → E is the same as A → (B → (C → (D → E))).def f : Nat → (Nat → ℝ) → ℝ → ℝ :=
fun (n : Nat) (u : Nat → ℝ) (x : ℝ) ↦ 2 ^ n * u n + x
def v : Nat → ℝ := fun n ↦ 2 * n
#eval f 3 v (-6) -- 42
The most famous inductive type in mathematics is probably the type Nat, whose definition goes back to Giuseppe Peano in 1889. In Lean, it is implemented as follows.
inductive Nat : Type
| zero : Nat
| succ : Nat → Nat
Inductive definitions produce special functions called constructors. In the present case, there are two of them (two introduction rules for terms of type Nat):
Nat.zero : Nat (a function whose value is its own name is called an atom, you can view it as a function from the Unit type to Nat, if you prefer).Nat.succ : Nat → Nat (the successor function), saying that for every n : Nat there is a term n.succ : Nat (dot notation for Nat.succ (n : Nat)).The fact that product types are defined inductively may seem less familiar to mathematicians.
inductive Prod (X Y : Type) : Type
| mk : X → Y → Prod X Y
This means that terms of type Prod X Y are introduced via the constructor Prod.mk : X → Y → Prod X Y. In other words, for all x : X and all y : Y, the term Prod.mk x y is of type Prod X Y and this is the only introduction rule for terms of type Prod X Y. In Lean, the type Prod X Y can be denoted by X × Y and its terms by ⟨x, y⟩ (angle brackets).
f : Nat → Nat, namely the factorial function. It was defined by induction. Since the constructor Nat.succ is a function from Nat to Nat, the induction principle for Nat includes a recursive call: to define f (n + 1), we may use n and f n, as in the definition of fact (n + 1).Nat: every inductive type has an associated induction principle. For the product, it implies in particular that, in order to define a function f : X × Y → Z, it suffices to define it on the canonical terms Prod.mk x y. In practice, this is done via pattern matching.def proj₁ {X Y : Type} : X × Y → X
| Prod.mk x y => x
inductive Sum (X Y : Type) : Type
| inl : X → Sum X Y
| inr : Y → Sum X Y
def characteristic_function_of_second_summand {X Y : Type} : X ⊕ Y → Bool
| Sum.inl x => (false : Bool)
| Sum.inr y => (true : Bool)
Piece of trivia: often seen in Lean examples,
As a matter of fact, the type of booleans is also an inductive type, with two constructors.