elan install stable
. To start a project, follow the instructions of the official Lean manual.There is a core 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 the standard references of 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 who you ask, a type may be seen as:
(3 : Nat)
, (-4 : ℤ)
, ([1, 2, 3] : List Nat)
, (Nat.mul : Nat → Nat → Nat)
.Every term has an assigned type, which is part of its definition as a term: since they do not have the same type, (3 : Nat)
and (3 : ℤ)
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 a term 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 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
.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.
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 X × Y
. In Lean, the type Prod X Y
is 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 charac_second_summand {X Y : Type} : X ⊕ Y → Bool
| Sum.inl x => (false : Bool)
| Sum.inr y => (true : Bool)
Lean syntax Natural Number Game
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.