отказ
Это очень неформально, как вы и просили.
Грамматика
В языке с зависимой типизацией у нас есть связующее на уровне типов, а также на уровне значений:
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)
Лямбда-абстракцию можно применять только к термину, если термин имеет тот же тип, что и переменная в связанном квантификаторе поиска. Затем мы уменьшаем как лямбда-абстракцию, так и квантификатор Форалла таким же образом, как в чистом лямбда-исчислении ранее. После вычитания части уровня значения мы получаем (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)
Я ставлю ?на условия, которые мы должны предоставить. Сначала мы явно представляем и создаем экземпляр 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)к конструкторам).
Кроме того, посмотрите на проще, проще! Сообщение блога.