Image credits: Assia Mahboubi.
Claim: It is possible to build a large amount of modern mathematics using type-theoretic foundations.
The general framework is provided by the Calculus Of Constructions, as well as the following three concepts from type theory:
Prop
type.Pi
types).To go further, one can also incorporate concepts from Homotopy type theory (HoTT
).
But first, what is a type?
A type is specified by its terms. For instance, most programming languages contain an implementation of the type of natural numbers ℕ
, whose terms are 0, 1, 2, ...
.
#check ℕ -- ℕ : Type
def n0 := (42 : ℕ)
#check n0 -- n0 : ℕ
#eval n0 -- 42
The example above uses the syntax of the Lean programming language, created by Leonardo de Moura in 2013. Basic computations are natively supported in Lean.
#eval 2 * n0 -- 84
If X
and Y
are types, there is a type X → Y
. Its terms are the functions from X
to Y
.
#check (ℕ → ℕ) -- ℕ → ℕ : Type
def f := fun (n : ℕ) => 2 * n
#check @f -- f : ℕ → ℕ
def f := λ (n : ℕ), 2 * n
would also be recognised by the compiler.
#check f -- f (n : ℕ) : ℕ
Functions can be evaluated on terms. The syntax #eval f(n0)
would not compile.
#eval f n0 -- 84
Natural numbers can be implemented as an inductive type using Peano's axiomatization.
inductive Nat :=
| zero : Nat
| succ (n : Nat) : Nat
So zero
is a term of type Nat
. And for all term n
of type Nat
, there is a term succ n
.
#check Nat.zero -- Nat.zero : Nat
#check @Nat.succ -- Nat.succ : Nat → Nat
The terms zero
and succ
are functions whose return type has not yet been declared (these are called constructors). The notation ℕ
and 0
is introduced a posteriori.
Because Nat
is declared as an inductive type, the compiler automatically generates a principle of induction. Note the occurence of the Prop
type, which requires further explanation (on the next slide!).
#check Nat.rec
-- Nat.rec {P : ℕ → Prop}
-- (zero : P 0)
-- (succ : ∀ (n : ℕ), P n → P (n + 1)) :
-- ∀ (t : ℕ), P t
In the code above, Nat.rec
is seen as a function that, in the presence of a predicate P : ℕ → Prop
, sends a proof of P 0
and a proof of the fact that, for all n : ℕ
, P n
implies P (n + 1)
, to a proof of P t
for all t : ℕ
.
Prop
typeProp
is the type whose terms are the formulas from first-order logic.
One starts from a language, with basic symbols such as +
or `` , and defines formulas inductively, following a set of rules. Terms of type Prop
are defined by such formulas.
def Example1 := ∀ n : ℕ, ∃ k : ℕ, 4 * n = 2 * k
#check Example1 -- Example1 : Prop
This is a well-defined mathematical statement, but we have not proved it yet.
Mathematically erroneous statements will type-check if they are syntactically correct.
def Example2 := ∀ n : ℕ, ∃ k : ℕ, 4 * n = 2 * k + 1
#check Example2 -- Example2 : Prop
To start doing mathematics, the idea is to view propositions as types, whose terms are proofs of that proposition. In proof theory, this is known as the Curry-Howard correspondence.
def MyFirstProof : ∀ n : ℕ, ∃ k : ℕ, 4 * n = 2 * k := sorry
Here, MyFirstProof
is a term of type ∀ n : ℕ, ∃ k : ℕ, 4 * n = 2 * k
.
4
are divisible by 2
To formalise a proof, one works backwards, reducing the goal to a known statement.
def MyFirstProof : ∀ n : ℕ, ∃ k : ℕ, 4 * n = 2 * k := by
intro n -- we introduce a natural number `n` in our local context
use (2 * n) -- we claim that we can use `k = 2 * n` to solve the goal
ring_nf -- we finish the proof by computation
Evolution of the tactic state:
∀ n : ℕ, ∃ k : ℕ, 4 * n = 2 * k -- goal at the beginning
∃ k : ℕ, 4 * n = 2 * k -- after intro n
4 * n = 2 * (2 * n) -- after use (2 * n)
No goals -- ring_nf closes the goal
Our term MyFirstProof
is recognised as a dependent function: it sends a natural number n
to a proof of a statement that depends on n
.
#check @MyFirstProof -- MyFirstProof : ∀ (n : ℕ), ∃ k, 4 * n = 2 * k
#check MyFirstProof -- MyFirstProof (n : ℕ) : ∃ k, 4 * n = 2 * k
The term MyFirstProof 2
is recognised as a proof of the proposition ∃ k, 4 * 2 = 2 * k
and we can use it as such.
#check MyFirstProof 2 -- MyFirstProof 2 : ∃ k, 4 * 2 = 2 * k
def EightIsEven : ∃ m : ℕ, 8 = 2 * m := MyFirstProof 2
Can we prove that addition of natural numbers is commutative?
theorem add_comm :
∀ (a b : ℕ), a + b = b + a
This is Level 3 of the Addition World in NNG 4.
Beyond technical difficulties (software installation and update), the use of proof assistants to do mathematics poses a number of challenges.
Let us define a sequence recursively in Lean (by pattern matching).
def u : ℕ → ℕ := fun n =>
match n with
| 0 => 2
| k + 1 => 2 * u k
And then we can prove simple results using the induction tactic.
example (n : ℕ) : u n = 2 ^ (n + 1) := by
induction n with
| zero => rw [u, pow_one]
| succ k ih => rw [succ_eq_add_one, u, ih]; ring_nf
However, type-theoretic difficulties can arise inadvertently (for instance with v
defined by v 0 = 1
and v (k + 1) = 2 * v k + 1
).
In Lean, equality is an inductive type. The declaration below means that, for all a : X
, refl a
is a proof of a = a
. Two terms are equal if and only if they are definitionally so.
inductive Eq {X : Type} : X → X → Prop
| refl (a : X) : Eq a a
We can then use the asociated induction principle to prove the symmetry and transitivity of the relation Eq
. Similarly, given a function f
, we can prove that a = b → f a = f b
.
example {X Y : Type} {f : X → Y} (a b : X) : (a = b) → f a = f b := by
intro h -- We introduce a term for the assumption that a = b
induction h -- We start a proof by induction on a term of type Eq
exact Eq.refl (f a)
Note that the induction lays on the term of inductive type h
, which is a proof that a = b
.
Note that a relation on a type X
is by definition a function Rel : X → X → Prop
as opposed to a subset of X × X
in set-theoretic mathematics.
theorem EqSymm {X : Type} (a b : X) : (a = b) → b = a := by
intro h
induction h
exact Eq.refl a
theorem EqTrans {X : Type} (a b c : X) : a = b → b = c → a = c := by
intro h1 h2
induction h1
exact h2
It is a good exercise to formalise what it means for a relation Rel
on a type X
to be reflexive, symmetric and transitive.
So, why not Lean into it? (^_^)/
induction a with k ih rw [add_zero] rw [zero_add] rfl rw [succ_add] rw [ih] rw [add_succ] rfl