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 t
tiene tipo σ
.
Reglas de mecanografía
En aras de la simplicidad es necesario que en λ v. t ∈ ∀ (v : σ). τ
tanto λ
y ∀
se unen la misma variable ( v
en 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 v
se introducen por ∀ (v : σ). τ
, entonces v
tienen 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
)
f
devuelve B y
para cualquier proporcionado y
de tipo A
. Aplicamos f
a x
, que es del tipo correcto A
, y el sustituto y
para x
el ∀
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 app
y 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 A
y 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
, B
y f
(donde f ∈ ∀ (y : A). B y
)
∀ (y : A). B y
app A B f
podemos instanciar A
y B
obtener (para cualquiera f
con 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 app
a 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
t
es *
entonces t ∈ *
por (1)
- Si
t
es ∀ (x : σ) τ
, σ ∈? *
, τ ∈? *
(véase la nota sobre ∈?
abajo) y luego t ∈ *
por (2)
- Si
t
es f x
, f ∈ ∀ (v : σ) τ
para algunos σ
y τ
, x ∈? σ
entonces t ∈ SUBS(τ, v, x)
por (4)
- Si
t
es una variable v
, v
fue 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
t
es λ v. b
y comprobado ∀ (v : σ) τ
, b ∈? τ
entoncest ∈ ∀ (v : σ) τ
- Si
t
es otra cosa y se compara σ
, deduzca el tipo de t
uso 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 t
tiene 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)). SUBS
tambié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 x
el tipo de f
, entonces x
se verifica con el tipo de argumento que f
recibe en lugar de inferirse y compararse para la igualdad (que también es menos eficiente). Pero si f
es 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 Term
o λ' (σ : Term) (v : Var)
a los constructores).
Además, eche un vistazo al más simple y fácil. entrada en el blog.