Відмова від відповідальності
Це дуже неформально, як ви просили.
Граматика
У мові, що залежить від тиску, у нас є сполучна на рівні типу, а також на рівні значень:
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)до конструкторів).
Крім того, подивіться на простіше, легше! блогпост.