D. Plaindoux | ScalaIO 2024
A dependent type is a type whose
definition depends on a value
All combinations with λC or Calculus of Constructions
P0 ... Pn when premisses P0 ... Pn are verified
--------- then
C we conclude C
P0 ... Pn when premisses P0 ... Pn are verified
--------- then
C we conclude C
Γ type bindings i.e. hypothesis
P0 ... Pn when premisses P0 ... Pn are verified
--------- then
C we conclude C
Γ type bindings (X : T) i.e. hypothesis
Γ ⊢ A : T Given Γ, the term A inhabits the type T
x ∈ Variables
i ∈ Identifiers
x ∈ Variables
i ∈ Identifiers
e ::=
λ(x).e -- Abstraction
e e -- Application
x -- Variable
x ∈ Variables
i ∈ Identifiers
e ::=
λ(x).e -- Abstraction
e e -- Application
x -- Variable
t ::=
i -- Basic types
t → t -- Function type
λ-expressions: abstraction and application
M → N
Γ, x : M ⊢ b : N
-------------------
Γ ⊢ λ(x).b : M → N
M → N
Γ, x : M ⊢ b : N
-------------------
Γ ⊢ λ(x).b : M → N
Γ ⊢ f : M → N Γ ⊢ e : M
--------------------------
Γ ⊢ f e : N
x ∈ Variables
i ∈ Identifiers
e ::=
λ(x).e -- Abstraction
e e -- Application
x -- Variables
t ::=
i -- Basic types (int, ...)
t → t -- Function type
x ∈ Variables
i ∈ Identifiers
e ::=
λ(x).e -- Abstraction
e e -- Application
x -- Variables
i -- Basic types (int, ...)
e → e -- Function type
x ∈ Variables
i ∈ Identifiers
e ::=
λ(x).e -- Abstraction
e e -- Application
x -- Variables
i -- Basic types (int, ...)
e → e -- Function type
Π(x:e).e -- Dependent functional type
type -- type of type
x ∈ Variables
i ∈ Identifiers
e ::=
λ(x).e -- Abstraction
e e -- Application
x -- Variables
i -- Basic types (int, ...)
e → e -- ≡ Π(_:e).e
Π(x:e).e -- Dependent functional type
type -- type of type
Π(x:M).N
type of return value varies with its argument
Γ, x : M ⊢ b : N
--------------------------------
Γ ⊢ λ(x).b : M → N
Γ ⊢ f : M → N Γ ⊢ e : M
--------------------------
Γ ⊢ f e : N
Π(x:M).N
type of return value varies with its argument
Γ, x : M ⊢ b : N
--------------------------------
Γ ⊢ λ(x).b : Π(_:M).N
Γ ⊢ f : Π(_:M).N Γ ⊢ e : M
-----------------------------
Γ ⊢ f e : N
Π(x:M).N
type of return value varies with its argument
Γ, x : M ⊢ b : N
--------------------------------
Γ ⊢ λ(x).b : Π(x:M).N
Γ ⊢ f : Π(_:M).N Γ ⊢ e : M
-----------------------------
Γ ⊢ f e : N
Π(x:M).N
type of return value varies with its argument
Γ, x : M ⊢ b : N Γ ⊢ M : type
--------------------------------
Γ ⊢ λ(x).b : Π(x:M).N
Γ ⊢ f : Π(_:M).N Γ ⊢ e : M
-----------------------------
Γ ⊢ f e : N
Π(x:M).N
type of return value varies with its argument
Γ, x : M ⊢ b : N Γ ⊢ M : type
--------------------------------
Γ ⊢ λ(x).b : Π(x:M).N
Γ ⊢ f : Π(x:M).N Γ ⊢ e : M
-----------------------------
Γ ⊢ f e : N
Π(x:M).N
type of return value varies with its argument
Γ, x : M ⊢ b : N Γ ⊢ M : type
--------------------------------
Γ ⊢ λ(x).b : Π(x:M).N
Γ ⊢ f : Π(x:M).N Γ ⊢ e : M
-----------------------------
Γ ⊢ f e : N[x:=e]
Π(x:M).N
type of return value varies with its argument
Γ, x : M ⊢ b : N Γ ⊢ M : type
--------------------------------
Γ ⊢ λ(x).b : Π(x:M).N
Γ ⊢ f : Π(x:M).N Γ ⊢ e : M
-----------------------------
Γ ⊢ f e : N[x:=e]
Γ ⊢ M : ? Γ, x : M ⊢ N : ?
-----------------------------------
Γ ⊢ Π(x:M).N : ?
Π(x:M).N
type of return value varies with its argument
Γ, x : M ⊢ b : N Γ ⊢ M : type
--------------------------------
Γ ⊢ λ(x).b : Π(x:M).N
Γ ⊢ f : Π(x:M).N Γ ⊢ e : M
-----------------------------
Γ ⊢ f e : N[x:=e]
Γ ⊢ M : type Γ, x : M ⊢ N : type
-----------------------------------
Γ ⊢ Π(x:M).N : type
-- A → B ≡ Π(_:A).B
sig id_t : ?
val id_t = λ(X).X → X
-- A → B ≡ Π(_:A).B
sig id_t : type → type
val id_t = λ(X).X → X
-- A → B ≡ Π(_:A).B
sig id_t : type → type
val id_t = λ(X).X → X
sig combine : Π(A:type).A → A → A
-- A → B ≡ Π(_:A).B
sig id_t : type → type
val id_t = λ(X).X → X
sig combine : Π(A:type).A → A → A
sig incr : int → int
val incr = combine int 1
-- A → B ≡ Π(_:A).B
sig id_t : type → type
val id_t = λ(X).X → X
sig combine : Π(A:type).A → A → A
sig incr : id_t int
val incr = combine int 1
-- A → B ≡ Π(_:A).B
sig select : int → type
-- with select 0 = string and
-- select i = char for i != 0
-- A → B ≡ Π(_:A).B
sig select : int → type
-- with select 0 = string and
-- select i = char for i != 0
sig aString : string
val aString = "Hello World 👋"
sig aChar : char
val aChar = '😀'
-- A → B ≡ Π(_:A).B
sig select : int → type
-- with select 0 = string and
-- select i = char for i != 0
sig aString : select 0
val aString = "Hello World 👋"
sig aChar : select 1
val aChar = '😀'
e ::=
...
e , e -- Pair
fst e -- Left projection
snd e -- Right projection
...
e * e -- Pair type
M * N
Γ ⊢ a : M Γ ⊢ b : N
----------------------
Γ ⊢ a , b : M * N
Γ ⊢ e : M * N Γ ⊢ e : M * N
-------------- ----------------
Γ ⊢ fst e : M Γ ⊢ snd e : N
e ::=
...
e , e -- Pair
fst e -- Left projection
snd e -- Right projection
...
e * e -- Pair type
e ::=
...
e , e -- Pair
fst e -- Left projection
snd e -- Right projection
...
e * e -- ≡ Σ(_:e).e
Σ(x:e).e -- Dependent pair type
Σ(x:M).N
type of right value varies with its left value
Γ ⊢ a : M Γ ⊢ b : N
-------------------------------------------
Γ ⊢ a , b : M * N
Γ ⊢ p : M * N Γ ⊢ p : M * N
------------- -----------------------
Γ ⊢ fst p : M Γ ⊢ snd p : N
Σ(x:M).N
type of right value varies with its left value
Γ ⊢ a : M Γ ⊢ b : N
-------------------------------------------
Γ ⊢ a , b : Σ(_:M).N
Γ ⊢ p : Σ(_:M).N Γ ⊢ p : Σ(_:M).N
----------------- -----------------------
Γ ⊢ fst p : M Γ ⊢ snd p : N
Σ(x:M).N
type of right value varies with its left value
Γ ⊢ a : M Γ ⊢ b : N
-------------------------------------------
Γ ⊢ a , b : Σ(x:M).N
Γ ⊢ p : Σ(_:M).N Γ ⊢ p : Σ(_:M).N
----------------- -----------------------
Γ ⊢ fst p : M Γ ⊢ snd p : N
Σ(x:M).N
type of right value varies with its left value
Γ ⊢ a : M Γ ⊢ b : N Γ ⊢ M : type
-------------------------------------------
Γ ⊢ a , b : Σ(x:M).N
Γ ⊢ p : Σ(_:M).N Γ ⊢ p : Σ(_:M).N
----------------- -----------------------
Γ ⊢ fst p : M Γ ⊢ snd p : N
Σ(x:M).N
type of right value varies with its left value
Γ ⊢ a : M Γ ⊢ b : N[x:=a] Γ ⊢ M : type
-------------------------------------------
Γ ⊢ a , b : Σ(x:M).N
Γ ⊢ p : Σ(_:M).N Γ ⊢ p : Σ(_:M).N
----------------- -----------------------
Γ ⊢ fst p : M Γ ⊢ snd p : N
Σ(x:M).N
type of right value varies with its left value
Γ ⊢ a : M Γ ⊢ b : N[x:=a] Γ ⊢ M : type
-------------------------------------------
Γ ⊢ a , b : Σ(x:M).N
Γ ⊢ p : Σ(x:M).N Γ ⊢ p : Σ(_:M).N
----------------- -----------------------
Γ ⊢ fst p : M Γ ⊢ snd p : N
Σ(x:M).N
type of right value varies with its left value
Γ ⊢ a : M Γ ⊢ b : N[x:=a] Γ ⊢ M : type
-------------------------------------------
Γ ⊢ a , b : Σ(x:M).N
Γ ⊢ p : Σ(x:M).N Γ ⊢ p : Σ(x:M).N
----------------- -----------------------
Γ ⊢ fst p : M Γ ⊢ snd p : N
Σ(x:M).N
type of right value varies with its left value
Γ ⊢ a : M Γ ⊢ b : N[x:=a] Γ ⊢ M : type
-------------------------------------------
Γ ⊢ a , b : Σ(x:M).N
Γ ⊢ p : Σ(x:M).N Γ ⊢ p : Σ(x:M).N
----------------- -----------------------
Γ ⊢ fst p : M Γ ⊢ snd p : N[x:=fst p]
Σ(x:M).N
type of right value varies with its left value
Γ ⊢ a : M Γ ⊢ b : N[x:=a] Γ ⊢ M : type
-------------------------------------------
Γ ⊢ a , b : Σ(x:M).N
Γ ⊢ p : Σ(x:M).N Γ ⊢ p : Σ(x:M).N
----------------- -----------------------
Γ ⊢ fst p : M Γ ⊢ snd p : N[x:=fst p]
Γ ⊢ M : type Γ, x : M ⊢ N : type
-----------------------------------
Γ ⊢ Σ(x:M).N : type
sig pair : ?
val pair = char , '🐶'
sig pair : type * char
val pair = char , '🐶'
sig pair : Σ(x:type).char
val pair = char , '🐶'
sig pair : Σ(x:type).x
val pair = char , '🐶'
sig pair : Σ(x:type).x
val pair = char , '🐶'
sig first : type
val first = fst pair
sig pair : Σ(x:type).x
val pair = char , '🐶'
sig first : type
val first = fst pair
sig second : ?
val second = snd pair
sig pair : Σ(x:type).x
val pair = char , '🐶'
sig first : type
val first = fst pair
sig second : char
val second = snd pair
sig pair : Σ(x:type).x
val pair = char , '🐶'
sig first : type
val first = fst pair
sig second : char -- char ≡ fst pair (i.e. x[x:=fst pair])
val second = snd pair
sig pair : Σ(x:type).x
val pair = (char , '🐶')
sig first : type
val first = fst pair
sig second : fst pair
val second = snd pair
x ∈ Variables
e ::=
...
inl e -- Left injection
inr e -- Right injection
case x e e -- Catamorphism
...
e + e -- Sum type
Γ ⊢ a : ? Γ ⊢ a : ?
------------------ ------------------
Γ ⊢ inl a : ? Γ ⊢ inr a : ?
Γ ⊢ a : ?
Γ ⊢ l : ? Γ ⊢ r : ?
--------------------------------------------------------
Γ ⊢ case a l r : P
Γ ⊢ a : M Γ ⊢ a : ?
------------------ ------------------
Γ ⊢ inl a : M + N Γ ⊢ inr a : ?
Γ ⊢ a : ?
Γ ⊢ l : ? Γ ⊢ r : ?
--------------------------------------------------------
Γ ⊢ case a l r : P
Γ ⊢ a : M Γ ⊢ a : N
------------------ ------------------
Γ ⊢ inl a : M + N Γ ⊢ inr a : M + N
Γ ⊢ a : ?
Γ ⊢ l : ? Γ ⊢ r : ?
--------------------------------------------------------
Γ ⊢ case a l r : P
Γ ⊢ a : M Γ ⊢ a : N
------------------ ------------------
Γ ⊢ inl a : M + N Γ ⊢ inr a : M + N
Γ ⊢ a : M + N
Γ ⊢ l : Π(x:M).P Γ ⊢ r : Π(x:N).P
--------------------------------------------------------
Γ ⊢ case a l r : P
Γ ⊢ a : M Γ ⊢ a : N
------------------ ------------------
Γ ⊢ inl a : M + N Γ ⊢ inr a : M + N
Γ ⊢ a : M + N
Γ ⊢ l : Π(x:M).P[a:=inl x] Γ ⊢ r : Π(x:N).P[a:=inr x]
--------------------------------------------------------
Γ ⊢ case a l r : P
Γ ⊢ a : M Γ ⊢ a : N
------------------ ------------------
Γ ⊢ inl a : M + N Γ ⊢ inr a : M + N
Γ ⊢ a : M + N
Γ ⊢ l : Π(x:M).P[a:=inl x] Γ ⊢ r : Π(x:N).P[a:=inr x]
--------------------------------------------------------
Γ ⊢ case a l r : P
Γ ⊢ M : type Γ ⊢ N : type
-----------------------------
Γ ⊢ M + N : type
sig int_or_char : int + char → type
val int_or_char = λ(x).case x λ(_).char λ(_).int
sig aChar : char
val aChar = '🌊'
sig int_or_char : int + char → type
val int_or_char = λ(x).case x λ(_).char λ(_).int
sig aChar : int_or_char (inl 1)
val aChar = '🌊'
sig bool : type
val bool = Unit + Unit
sig bool : type
val bool = Unit + Unit
sig true : bool
val true = inl unit
sig false : bool
val false = inr unit
sig bool : type
val bool = Unit + Unit
sig true : bool
val true = inl unit
sig false : bool
val false = inr unit
sig Test : bool → type
val Test = λ(b).case b λ(_).int λ(_).char
sig bool : type
val bool = Unit + Unit
sig true : bool
val true = inl unit
sig false : bool
val false = inr unit
sig Test : bool → type
val Test = λ(b).case b λ(_).int λ(_).char
sig test : Π(b:bool).?
val test = λ(c).case c λ(_).1 λ(c).'🫣'
sig bool : type
val bool = Unit + Unit
sig true : bool
val true = inl unit
sig false : bool
val false = inr unit
sig Test : bool → type
val Test = λ(b).case b λ(_).int λ(_).char
sig test : Π(b:bool).(Test b)
val test = λ(c).case c λ(_).1 λ(c).'🫣'
Intentional type theory
e ::=
...
refl -- Reflexivity
...
e = e -- type equality
Intentional type theory
e ::=
...
refl -- Reflexivity
subst e by e -- Substitution
...
e = e -- type equality
Γ ⊢ n : A Γ ⊢ m : A
-----------------------
Γ ⊢ n = m : type
Γ ⊢ n : A Γ ⊢ m : A
----------------------- -----------------
Γ ⊢ n = m : type Γ ⊢ refl : m = m
Γ ⊢ n : A Γ ⊢ m : A
----------------------- -----------------
Γ ⊢ n = m : type Γ ⊢ refl : m = m
Γ ⊢ b : x = B Γ ⊢ a : A[x:=B]
---------------------------------
Γ ⊢ subst a by b : A
Γ ⊢ b : B = x Γ ⊢ a : A[x:=B]
---------------------------------
Γ ⊢ subst a by b : A
sig reflexive : Π(A:type).Π(a:A).(a = a)
val reflexive = λ(_).λ(a).refl
sig reflexive : Π(A:type).Π(a:A).(a = a)
val reflexive = λ(_).λ(a).refl
sig symmetric : Π(A:type).Π(a:A).Π(b:A).(a = b → b = a)
val symmetric = λ(_).λ(a).λ(b).λ(a=b).?
sig reflexive : Π(A:type).Π(a:A).(a = a)
val reflexive = λ(_).λ(a).refl
sig symmetric : Π(A:type).Π(a:A).Π(b:A).(a = b → b = a)
val symmetric = λ(_).λ(a).λ(b).λ(a=b).(subst refl by a=b)
-{
data Expr A =
| Boolean of bool with A = bool
| Integer of int with A = int
}-
sig Expr : type → type
val Expr = ?
-{
data Expr A =
| Boolean of bool with A = bool
| Integer of int with A = int
}-
sig Expr : type → type
val Expr = λ(A).((A = bool) * bool) + ((A = int) * int)
sig Expr : type → type
val Expr = λ(A).((A = bool) * bool) + ((A = int) * int)
sig Expr : type → type
val Expr = λ(A).((A = bool) * bool) + ((A = int) * int)
sig Boolean : Π(A:type).Π(_:A = bool).(bool -> Expr A)
val Boolean = λ(_).λ(p).λ(b).inl (p,b)
sig Expr : type → type
val Expr = λ(A).((A = bool) * bool) + ((A = int) * int)
sig Boolean : Π(A:type).Π(_:A = bool).(bool -> Expr A)
val Boolean = λ(_).λ(p).λ(b).inl (p,b)
sig Integer : Π(A:type).Π(_:A = int).(int -> Expr A)
val Integer = λ(_).λ(p).λ(i).inr (p,i)
sig Expr : type → type
val Expr = λ(A).((A = bool) * bool) + ((A = int) * int)
sig Boolean : Π(A:type).Π(_:A = bool).(bool -> Expr A)
val Boolean = λ(_).λ(p).λ(b).inl (p,b)
sig Integer : Π(A:type).Π(_:A = int).(int -> Expr A)
val Integer = λ(_).λ(p).λ(i).inr (p,i)
sig one_expr : Expr int
val one_expr : Integer int refl 1
Think different!