Відмова від відповідальності
Це дуже неформально, як ви просили.
Граматика
У мові, що залежить від тиску, у нас є сполучна на рівні типу, а також на рівні значень:
Term = * | (∀ (Var : Term). Term) | (Term Term) | (λ Var. Term) | Var
Добре набраний термін - це термін із доданим типом, ми напишемо t ∈ σ
або
σ
t
щоб вказати, що термін t
має тип σ
.
Правила введення тексту
Для простоти ми вимагаємо, щоб в λ v. t ∈ ∀ (v : σ). τ
обох λ
і ∀
прив’язували ту саму змінну ( v
в даному випадку).
Правила:
t ∈ σ is well-formed if σ ∈ * and t is in normal form (0)
* ∈ * (1)
∀ (v : σ). τ ∈ * -: σ ∈ *, τ ∈ * (2)
λ v. t ∈ ∀ (v : σ). τ -: t ∈ τ (3)
f x ∈ SUBS(τ, v, x) -: f ∈ ∀ (v : σ). τ, x ∈ σ (4)
v ∈ σ -: v was introduced by ∀ (v : σ). τ (5)
Таким чином, *
є "тип усіх типів" (1), ∀
утворює типи з типів (2), лямбда-абстракції мають pi-типи (3), а якщо v
вводиться ∀ (v : σ). τ
, то v
має тип σ
(5).
"у звичайній формі" означає, що ми виконуємо якомога більше скорочень за допомогою правила скорочення:
Правило скорочення
(λ v. b ∈ ∀ (v : σ). τ) (t ∈ σ) ~> SUBS(b, v, t) ∈ SUBS(τ, v, t)
where `SUBS` replaces all occurrences of `v`
by `t` in `τ` and `b`, avoiding name capture.
Або в двовимірному синтаксисі де
σ
t
означає t ∈ σ
:
(∀ (v : σ). τ) σ SUBS(τ, v, t)
~>
(λ v . b) t SUBS(b, v, t)
Застосувати лямбда-абстракцію до терміна можна лише тоді, коли термін має той самий тип, що і змінна у відповідному кількісному показнику forall. Тоді ми зменшуємо абстракцію лямбда та квантор forall так само, як і раніше чисте лямбда-числення. Віднімаючи частину рівня значення, отримуємо правило (4) набору тексту.
Приклад
Ось оператор додатка функції:
∀ (A : *) (B : A -> *) (f : ∀ (y : A). B y) (x : A). B x
λ A B f x . f x
(Ми скорочуємо , ∀ (x : σ). τ
щоб σ -> τ
якщо τ
не згадувати x
)
f
повертає B y
для будь-якого наданого y
типу A
. Застосуємо f
до x
, що правильний тип A
, і замінити y
на x
в ∀
AFTER .
, таким чином , f x ∈ SUBS(B y, y, x)
~> f x ∈ B x
.
Тепер скоротимо оператор додатка функції як app
і застосуємо його до себе:
∀ (A : *) (B : A -> *). ?
λ A B . app ? ? (app A B)
Я розміщую ?
умови, які нам потрібно надати. По- перше , ми явно вводимо і Instantiate A
і B
:
∀ (f : ∀ (y : A). B y) (x : A). B x
app A B
Тепер нам потрібно уніфікувати те, що ми маємо
∀ (f : ∀ (y : A). B y) (x : A). B x
що те саме
(∀ (y : A). B y) -> ∀ (x : A). B x
і що app ? ?
отримує
∀ (x : A'). B' x
Це призводить до
A' ~ ∀ (y : A). B y
B' ~ λ _. ∀ (x : A). B x -- B' ignores its argument
(див. також Що таке предикативність? )
Наш вираз (після деякого перейменування) стає
∀ (A : *) (B : A -> *). ?
λ A B . app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B)
Так як для будь-якого A
, B
і f
(де f ∈ ∀ (y : A). B y
)
∀ (y : A). B y
app A B f
ми можемо ініціювати A
та B
отримати (для будь-якого f
з відповідним типом)
∀ (y : ∀ (x : A). B x). ∀ (x : A). B x
app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) f
і підпис типу еквівалентний (∀ (x : A). B x) -> ∀ (x : A). B x
.
Весь вираз є
∀ (A : *) (B : A -> *). (∀ (x : A). B x) -> ∀ (x : A). B x
λ A B . app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B)
Тобто
∀ (A : *) (B : A -> *) (f : ∀ (x : A). B x) (x : A). B x
λ A B f x .
app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B) f x
що після всіх зменшень на рівні значень дає ту саму app
назад.
Таким чином , в той час як він вимагає всього кілька кроків в чистому лямбда - обчисленні , щоб отримати app
відapp app
, в типизированной настройки (і особливо в залежності надрукував) ми також повинні дбати про об'єднання і речі стають все більш складними , навіть з деяким inconsitent зручності ( * ∈ *
).
Перевірка типу
- Якщо
t
є , *
тоt ∈ *
з (1)
- Якщо
t
є ∀ (x : σ) τ
, σ ∈? *
, τ ∈? *
(дивіться примітка про ∈?
нижче) , тоt ∈ *
в силу (2)
- Якщо
t
є f x
, f ∈ ∀ (v : σ) τ
для деяких σ
і τ
,x ∈? σ
то в t ∈ SUBS(τ, v, x)
силу (4)
- Якщо
t
це змінна v
, v
введена до того ∀ (v : σ). τ
часуt ∈ σ
(5)
Це все правила виводу, але ми не можемо зробити те ж саме для лямбдів (умовивід про тип не залежить від залежних типів). Тож для лямбдів перевіряємо (t ∈? σ
), а не висновок:
- Якщо
t
є λ v. b
і перевірено ∀ (v : σ) τ
,b ∈? τ
тоt ∈ ∀ (v : σ) τ
- Якщо
t
це щось інше і перевірено, σ
тоді слід зробити висновок про тип t
використання вищезгаданої функції та перевірити, чи є вонаσ
Перевірка рівності для типів вимагає, щоб вони були у звичайних формах, тому для вирішення, чи t
є тип, σ
ми спочатку перевіряємо, що σ
має тип *
. Якщо так, то σ
це нормалізується (парадокс модуля Жирара) і він нормалізується (відтак σ
стає добре сформованим через (0)). SUBS
також нормалізує вирази для збереження (0).
Це називається двонаправлена перевірка типу. З його допомогою нам не потрібно коментувати кожну лямбда з типом: якщо f x
тип типу f
відомий, то x
він перевіряється на тип аргументу, який f
отримує, замість того, щоб робити висновок і порівнювати за рівність (що також менш ефективно). Але якщо f
це лямбда, вона вимагає явного анотації типу (анотації опущені в граматиці та скрізь, ви можете додатиAnn Term Term
або λ' (σ : Term) (v : Var)
до конструкторів).
Крім того, подивіться на простіше, легше! блогпост.