aviso Legal
Isso é muito informal, como você solicitou.
A gramática
Em uma linguagem de tipo dependente, temos um fichário no nível do tipo e no nível do valor:
Term = * | (∀ (Var : Term). Term) | (Term Term) | (λ Var. Term) | Var
Termo bem digitado é um termo com tipo anexado, escreveremos t ∈ σou
σ
t
para indicar que o termo ttem tipo σ.
Regras de digitação
Por uma questão de simplicidade, exigimos isso em λ v. t ∈ ∀ (v : σ). τambos λe ∀vinculamos a mesma variável ( vneste caso).
Regras:
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)
Assim, *é "o tipo de todos os tipos" (1), ∀forma tipos a partir de tipos (2), abstrações lambda têm tipos pi (3) e, se vintroduzido por ∀ (v : σ). τ, então vpossui o tipo σ(5).
"na forma normal" significa que realizamos o maior número possível de reduções usando a regra de redução:
Regra de redução "A"
(λ 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.
Ou na sintaxe bidimensional em que
σ
t
significa t ∈ σ:
(∀ (v : σ). τ) σ SUBS(τ, v, t)
~>
(λ v . b) t SUBS(b, v, t)
Só é possível aplicar uma abstração lambda a um termo quando o termo tiver o mesmo tipo que a variável no quantificador associado a todos. Em seguida, reduzimos a abstração lambda e o quantificador forall da mesma maneira que no cálculo lambda puro anterior. Após subtrair a parte do nível de valor, obtemos a regra de digitação (4).
Um exemplo
Aqui está o operador do aplicativo de funções:
∀ (A : *) (B : A -> *) (f : ∀ (y : A). B y) (x : A). B x
λ A B f x . f x
(abreviamos ∀ (x : σ). τse σ -> τif τnão menciona x)
fretorna B ypara qualquer fornecido ydo tipo A. Nós aplicamos fpara x, que é do tipo certo A, e substituto ypara xno ∀depois ., assim f x ∈ SUBS(B y, y, x)~> f x ∈ B x.
Vamos agora abreviar o operador de aplicativo de função como appe aplicá-lo a si mesmo:
∀ (A : *) (B : A -> *). ?
λ A B . app ? ? (app A B)
Coloco ?termos que precisamos fornecer. Primeiro, apresentamos e instanciamos explicitamente Ae B:
∀ (f : ∀ (y : A). B y) (x : A). B x
app A B
Agora precisamos unificar o que temos
∀ (f : ∀ (y : A). B y) (x : A). B x
que é o mesmo que
(∀ (y : A). B y) -> ∀ (x : A). B x
e o que app ? ?recebe
∀ (x : A'). B' x
Isto resulta em
A' ~ ∀ (y : A). B y
B' ~ λ _. ∀ (x : A). B x -- B' ignores its argument
(veja também O que é predicatividade? )
Nossa expressão (depois de renomear) se torna
∀ (A : *) (B : A -> *). ?
λ A B . app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B)
Desde para qualquer A, Be f(onde f ∈ ∀ (y : A). B y)
∀ (y : A). B y
app A B f
podemos instanciar Ae Bobter (para qualquer um fdo tipo apropriado)
∀ (y : ∀ (x : A). B x). ∀ (x : A). B x
app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) f
e a assinatura do tipo é equivalente a (∀ (x : A). B x) -> ∀ (x : A). B x.
Toda a expressão é
∀ (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)
Ou seja,
∀ (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
que depois de todas as reduções no nível de valor dá o mesmo appretorno.
Então, enquanto ele requer apenas alguns passos no cálculo lambda pura para obter appa partir de app app, em um ambiente digitada (e especialmente um dependente digitado) também precisa se preocupar com a unificação e as coisas se tornam mais complexas, mesmo com alguma conveniência inconsistentes ( * ∈ *).
Verificação de tipo
- Se
tfor *então t ∈ *por (1)
- Se
té ∀ (x : σ) τ, σ ∈? *, τ ∈? *(veja a nota sobre ∈?a seguir), em seguida, t ∈ *por (2)
- Se
tfor f x, f ∈ ∀ (v : σ) τpara alguns σe τ, x ∈? σentão , t ∈ SUBS(τ, v, x)por (4)
- Se
té uma variável v, vfoi introduzido até ∀ (v : σ). τentão t ∈ σpor (5)
Todas essas são regras de inferência, mas não podemos fazer o mesmo com lambdas (a inferência de tipo é indecidível para tipos dependentes). Portanto, para lambdas, verificamos ( t ∈? σ) em vez de inferir:
- Se
tfor λ v. be verificado contra ∀ (v : σ) τ, b ∈? τentãot ∈ ∀ (v : σ) τ
- Se
thouver outra coisa com a qual σcomparar, deduza o tipo de tuso da função acima e verifique se estáσ
A verificação de igualdade para tipos requer que eles estejam em formas normais, para decidir se ttem um tipo σ, primeiro verificamos se σhá um tipo *. Nesse caso, σé normalizável (paradoxo do módulo Girard) e é normalizado (portanto, σtorna-se bem formado por (0)). SUBStambém normaliza expressões para preservar (0).
Isso é chamado de verificação de tipo bidirecional. Com ele, não precisamos anotar todos os lambda com um tipo: se f xo tipo de ffor conhecido, xserá verificado o tipo de argumento frecebido em vez de inferido e comparado pela igualdade (que também é menos eficiente). Mas se ffor um lambda, ele exige uma anotação de tipo explícita (as anotações são omitidas na gramática e em todo lugar, você pode adicionar Ann Term Termou λ' (σ : Term) (v : Var)aos construtores).
Além disso, dê uma olhada no mais simples, mais fácil! blogpost.