Renuncia
Esto es muy informal, como lo solicitó.
La gramática
En un lenguaje mecanografiado de forma dependiente, tenemos una carpeta en el nivel de tipo, así como en el nivel de valor:
Term = * | (∀ (Var : Term). Term) | (Term Term) | (λ Var. Term) | Var
El término bien escrito es un término con tipo adjunto, escribiremos t ∈ σo
σ
t
para indicar que el término ttiene tipo σ.
Reglas de mecanografía
En aras de la simplicidad es necesario que en λ v. t ∈ ∀ (v : σ). τtanto λy ∀se unen la misma variable ( ven este caso).
Reglas:
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)
Por lo tanto, *es "el tipo de todos los tipos" (1), los ∀tipos de formas de los tipos (2), las abstracciones lambda tienen tipos pi (3) y si vse introducen por ∀ (v : σ). τ, entonces vtienen el tipo σ(5).
"en forma normal" significa que realizamos tantas reducciones como sea posible utilizando la regla de reducción:
"La" regla de reducción
(λ 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.
O en sintaxis bidimensional donde
σ
t
significa t ∈ σ:
(∀ (v : σ). τ) σ SUBS(τ, v, t)
~>
(λ v . b) t SUBS(b, v, t)
Solo es posible aplicar una abstracción lambda a un término cuando el término tiene el mismo tipo que la variable en el cuantificador general asociado. Luego reducimos tanto la abstracción lambda como el cuantificador total de la misma manera que en el cálculo lambda puro anterior. Después de restar la parte del nivel de valor, obtenemos la regla de escritura (4).
Un ejemplo
Aquí está el operador de la aplicación de función:
∀ (A : *) (B : A -> *) (f : ∀ (y : A). B y) (x : A). B x
λ A B f x . f x
(abreviamos ∀ (x : σ). τa σ -> τsi τno se menciona x)
fdevuelve B ypara cualquier proporcionado yde tipo A. Aplicamos fa x, que es del tipo correcto A, y el sustituto ypara xel ∀después ., por lo tanto f x ∈ SUBS(B y, y, x)~> f x ∈ B x.
Vamos a abreviar ahora el operador de aplicación de función como appy aplicarlo a sí mismo:
∀ (A : *) (B : A -> *). ?
λ A B . app ? ? (app A B)
Coloco los ?términos que necesitamos proporcionar. Primero introducimos e instanciamos explícitamente Ay B:
∀ (f : ∀ (y : A). B y) (x : A). B x
app A B
Ahora necesitamos unificar lo que tenemos
∀ (f : ∀ (y : A). B y) (x : A). B x
que es lo mismo que
(∀ (y : A). B y) -> ∀ (x : A). B x
y lo que app ? ?recibe
∀ (x : A'). B' x
Esto resulta en
A' ~ ∀ (y : A). B y
B' ~ λ _. ∀ (x : A). B x -- B' ignores its argument
(ver también ¿Qué es la predicatividad? )
Nuestra expresión (después de un cambio de nombre) se convierte en
∀ (A : *) (B : A -> *). ?
λ A B . app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B)
Dado que para cualquiera A, By f(donde f ∈ ∀ (y : A). B y)
∀ (y : A). B y
app A B f
podemos instanciar Ay Bobtener (para cualquiera fcon el tipo apropiado)
∀ (y : ∀ (x : A). B x). ∀ (x : A). B x
app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) f
y la firma de tipo es equivalente a (∀ (x : A). B x) -> ∀ (x : A). B x.
Toda la expresión es
∀ (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)
Es decir
∀ (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 después de todas las reducciones en el nivel de valor devuelve lo mismo app.
Así, mientras que requiere sólo unos pasos en el cálculo lambda pura para obtener appa partir app app, en un entorno mecanografiada (y sobre todo una manera dependiente mecanografiadas) también hay que preocuparse por la unificación y las cosas se vuelven más complejas, incluso con cierta comodidad inconsitent ( * ∈ *).
Comprobación de tipo
- Si
tes *entonces t ∈ *por (1)
- Si
tes ∀ (x : σ) τ, σ ∈? *, τ ∈? *(véase la nota sobre ∈?abajo) y luego t ∈ *por (2)
- Si
tes f x, f ∈ ∀ (v : σ) τpara algunos σy τ, x ∈? σentonces t ∈ SUBS(τ, v, x)por (4)
- Si
tes una variable v, vfue introducido por ∀ (v : σ). τentonces t ∈ σpor (5)
Todas estas son reglas de inferencia, pero no podemos hacer lo mismo para lambdas (la inferencia de tipos es indecidible para los tipos dependientes). Entonces, para lambdas, verificamos ( t ∈? σ) en lugar de inferir:
- Si
tes λ v. by comprobado ∀ (v : σ) τ, b ∈? τentoncest ∈ ∀ (v : σ) τ
- Si
tes otra cosa y se compara σ, deduzca el tipo de tuso de la función anterior y verifique si esσ
La verificación de igualdad de tipos requiere que estén en formas normales, por lo que para decidir si ttiene tipo σ, primero verificamos que σtenga tipo *. Si es así, entonces σes normalizable (paradoja del módulo Girard) y se normaliza (por lo tanto, σestá bien formado por (0)). SUBStambién normaliza expresiones para preservar (0).
Esto se llama verificación de tipo bidireccional. Con él no necesitamos anotar cada lambda con un tipo: si se conoce f xel tipo de f, entonces xse verifica con el tipo de argumento que frecibe en lugar de inferirse y compararse para la igualdad (que también es menos eficiente). Pero si fes un lambda, requiere un tipo de anotación explícita (anotaciones se omiten en la gramática y en todas partes, puede agregar Ann Term Termo λ' (σ : Term) (v : Var)a los constructores).
Además, eche un vistazo al más simple y fácil. entrada en el blog.