Nivel 5 · El cubo-λ

λP: tipos dependientes

El corazón de la matemática reside en la lógica de predicados, que se basa en proposiciones cuya verdad depende de valores concretos. No se puede expresar P(n):=P(n) := «nn es un número par» si el tipo que representa a la proposición PP no puede depender del término nn.

Se necesita cerrar esta brecha: se requiere que los tipos (que representan proposiciones) puedan depender de los términos. Esta es la extensión que define el Sistema λP\lambda P. Al permitir esta dependencia, el sistema proporciona una forma de codificar directamente la cuantificación universal de la lógica: la afirmación x:A,P(x)\forall x{:}A,\, P(x) se convierte en el tipo Πx:A.P(x)\Pi x{:}A.\, P(x). Este es el paso que eleva la teoría de tipos de una simple lógica de proposiciones a una lógica robusta de predicados, sentando las bases para la formalización de la matemática. La presentación sigue a Nederpelt y Geuvers (2014) .

Tipos que dependen de términos

La idea central de λP\lambda P es permitir que las familias de tipos sean indexadas no por otros tipos (como en λω\lambda\underline{\omega}), sino por términos: un tipo puede cambiar su forma en función de un valor.

Ejemplo 8.23 (Vectores de longitud fija)

Un ejemplo computacional clásico es el de un vector de longitud fija. Se desea tener un tipo, Vec\texttt{Vec}, que dependa no solo del tipo de sus elementos (por ejemplo nat\texttt{nat}), sino también de un valor numérico que especifique su longitud. Así se tendrían tipos como:

  • Vec nat 0\texttt{Vec}\ \texttt{nat}\ 0: el tipo de los vectores de naturales de longitud 00 (la lista vacía).
  • Vec nat 3\texttt{Vec}\ \texttt{nat}\ 3: el tipo de los vectores de naturales de longitud 33 (como [1,2,3][1, 2, 3]).
  • Vec bool 2\texttt{Vec}\ \texttt{bool}\ 2: el tipo de los vectores de booleanos de longitud 22 (como [true,false][\mathrm{true}, \mathrm{false}]).

Fijado el tipo de los elementos, el constructor Vecbool\texttt{Vec}_{\texttt{bool}} tiene el kind nat\texttt{nat} \to *: una familia de tipos indexada por términos. (La versión totalmente genérica Vec:nat\texttt{Vec} : * \to \texttt{nat} \to * requiere además la dependencia (,)(\square, \square), y pertenece a λPω\lambda P\underline{\omega} o λC\lambda C, no a λP\lambda P.)

Ejemplo 8.24 (Predicados como familias de tipos)

Esta misma idea se traduce directamente al lenguaje de la lógica. Un predicado, como «ser un número par», puede ser visto como una familia de proposiciones (tipos) indexada por los números naturales:

EsPar:nat.\texttt{EsPar} : \texttt{nat} \to *.

Al aplicar este predicado a un término se obtiene un tipo específico:

  • EsPar 2\texttt{EsPar}\ 2: el tipo que representa la proposición «2 es par». Este tipo está habitado (es decir, existe una prueba para él).
  • EsPar 3\texttt{EsPar}\ 3: el tipo que representa la proposición «3 es par». Este tipo está vacío (no existe una prueba para él).

La lógica de predicados como un sistema de tipos

La verdadera fuerza de λP\lambda P se revela cuando se aplica la correspondencia de proposiciones como tipos (PAT, por sus siglas en inglés de Propositions As Types, la correspondencia de Curry–Howard del módulo anterior) a este nuevo poder de dependencia. Esto permite codificar la lógica de predicados de primer orden directamente en el sistema de tipos.

Definición 8.25 (La correspondencia PAT para lógica de predicados)

La correspondencia se extiende de la siguiente manera:

  • Proposiciones: se representan como tipos de kind *.
  • Pruebas de proposiciones: se representan como términos que habitan el tipo correspondiente.
  • Predicados sobre un conjunto SS (por ejemplo nat\texttt{nat}): se representan como constructores de tipos de la forma SS \to *.
  • La implicación (ABA \Rightarrow B): se representa con el tipo función ABA \to B. Probar ABA \Rightarrow B es construir un término de tipo ABA \to B.
  • La cuantificación universal (xS,P(x)\forall x \in S,\, P(x)): se representa con el tipo producto dependiente Πx:S.Px\Pi x{:}S.\, P x. Probar esta afirmación es construir un término (una función dependiente) que, para cualquier término a:Sa : S, produce un término (una prueba) de tipo PaP a.

Bajo esta interpretación, las reglas de inferencia de la lógica aparecen de forma natural a partir de las reglas de tipado del cálculo. Por ejemplo, la regla de eliminación para \forall (especialización) es simplemente la regla de aplicación de una función dependiente, y la regla de introducción (generalización) es la regla de abstracción.

Ejemplo: construyendo una prueba formal

Para ver el sistema en acción, se construirá una prueba formal para un teorema simple de la lógica de predicados. Se considera la siguiente proposición: si QQ es verdadero para todos los pares (x,y)(x, y), entonces QQ es verdadero para todos los pares de la forma (u,u)(u, u). Formalmente:

xSyS(Q(x,y))uS(Q(u,u)).\forall_{x \in S}\, \forall_{y \in S}\, (Q(x, y)) \Rightarrow \forall_{u \in S}\, (Q(u, u)).

El objetivo es construir un término que habite el tipo correspondiente en λP\lambda P.

Objetivo: encontrar un habitante para el tipo

(Πx:S.Πy:S.Qxy)(Πu:S.Quu),(\Pi x{:}S.\, \Pi y{:}S.\, Q\,x\,y) \rightarrow (\Pi u{:}S.\, Q\,u\,u),

donde S:S : * es un conjunto y Q:SSQ : S \to S \to * es un predicado de dos argumentos. Usando la notación de banderas, y siguiendo la sección 5.5 de Nederpelt y Geuvers (2014) , se tiene:

(a) S:S : *
(b) Q:SSQ : S \rightarrow S \rightarrow *
(c) z:Πx:S.Πy:S.Qxyz : \Pi x{:}S.\, \Pi y{:}S.\, Q\,x\,y
(d) u:Su : S
(1) zu:Πy:S.Quyz\,u : \Pi y{:}S.\, Q\,u\,y (aplicación) en (c) y (d)
(2) zuu:Quuz\,u\,u : Q\,u\,u (aplicación) en (1) y (d)
(3) λu:S.zuu  :  Πu:S.Quu\lambda u{:}S.\, z\,u\,u \;:\; \Pi u{:}S.\, Q\,u\,u (abstracción) en (2)
(4) λz:(Πx:S.Πy:S.Qxy).λu:S.zuu  :  (Πx:S.Πy:S.Qxy)(Πu:S.Quu)\lambda z{:}(\Pi x{:}S.\, \Pi y{:}S.\, Q\,x\,y).\, \lambda u{:}S.\, z\,u\,u \;:\; (\Pi x{:}S.\, \Pi y{:}S.\, Q\,x\,y) \rightarrow (\Pi u{:}S.\, Q\,u\,u) (abstracción) en (3)

Cada paso se justifica así: en (1), la hipótesis zz (una prueba de que QQ vale para todos los pares) se aplica al término uu, especializando el primer cuantificador; en (2) se aplica de nuevo a uu, especializando el segundo; en (3) la abstracción sobre uu descarga la bandera (d) e introduce el cuantificador Πu:S\Pi u{:}S; y en (4) la abstracción sobre zz descarga la bandera (c) e introduce la implicación. El término final, λz.λu.zuu\lambda z.\, \lambda u.\, z\,u\,u, es el habitante que se buscaba. En general es un algoritmo: una construcción basada en los principios lógicos.

Las reglas Π de λP

El sistema λP\lambda P se formaliza con un conjunto de reglas del mismo esquema que el de λω\lambda\underline{\omega} (sort, variable, debilitamiento, formación, aplicación, abstracción, conversión), sustituyendo el conector \rightarrow por el producto dependiente Π\Pi. Nótese que λP\lambda P no contiene a λω\lambda\underline{\omega}: en λP\lambda P no se forman kinds como * \rightarrow *, pues carece de la dependencia (,)(\square, \square). Las tres reglas donde aparece el producto dependiente son:

(formacioˊn)ΓA:Γ, x:AB:sΓΠx:A.B:s(aplicacioˊn)ΓM:Πx:A.BΓN:AΓMN:B[x:=N]\text{(formación)} \quad \frac{\Gamma \vdash A : * \quad \Gamma,\ x : A \vdash B : s}{\Gamma \vdash \Pi x{:}A.\, B : s} \qquad \text{(aplicación)} \quad \frac{\Gamma \vdash M : \Pi x{:}A.\, B \quad \Gamma \vdash N : A}{\Gamma \vdash M\, N : B[x := N]} (abstraccioˊn)Γ, x:AM:BΓΠx:A.B:sΓλx:A.M:Πx:A.B\text{(abstracción)} \quad \frac{\Gamma,\ x : A \vdash M : B \quad \Gamma \vdash \Pi x{:}A.\, B : s}{\Gamma \vdash \lambda x{:}A.\, M : \Pi x{:}A.\, B}

La regla (abstracción) es la que justifica los pasos (3) y (4) de la derivación de banderas anterior. La regla (formación) permite crear tipos dependientes Π\Pi; la restricción clave de λP\lambda P es que la dependencia debe ser sobre una variable xx de un tipo simple (A:A : *). Sin embargo, la construcción es general: el tipo resultante Πx:A.B\Pi x{:}A.\, B es de sort ss, lo que significa que λP\lambda P no solo formaliza proposiciones que dependen de términos (el caso ss \equiv *), sino también kinds que dependen de términos (el caso ss \equiv \square), como el kind SS \to * de los predicados de la Definición 8.25.

Ejemplo en Lean

La sección teórica culmina con la prueba del teorema

(Πx:S.Πy:S.Qxy)(Πu:S.Quu).(\Pi x{:}S.\, \Pi y{:}S.\, Q\,x\,y) \rightarrow (\Pi u{:}S.\, Q\,u\,u).

El término-prueba que se construye, λz.λu.zuu\lambda z.\, \lambda u.\, z\,u\,u, se traduce de manera casi literal en Lean. Aquí forall es la notación de Lean para el tipo producto dependiente Π\Pi y Prop es el tipo de todas las proposiciones (el análogo a * en este contexto):

-- S es un tipo cualquiera (un conjunto).
-- Q es un predicado que toma dos elementos de S y devuelve una proposición.
def propiedad {S : Type} (Q : S -> S -> Prop)
-- La hipótesis: para todo x, y en S, Q x y es verdadero.
(h : forall x y : S, Q x y)
-- La conclusión: para todo u en S, Q u u es verdadero.
: (forall u : S, Q u u) :=
-- La prueba: dado un u, se usa la hipótesis 'h' con 'u' dos veces.
fun u : S => h u u

En este ejemplo, fun cumple el papel del operador λ\lambda; es la forma rápida de Lean para decir: «toma un u:Su : S y devuelve huuh\,u\,u». Así, fun u : S => h u u:

  1. Introduce la variable uu (equivale a escribir λu:S.\lambda u{:}S.\, \ldots).
  2. Construye el resultado aplicando la hipótesis global hh al par (u,u)(u, u).
  3. Devuelve esa prueba, produciendo la función uhuu  :  Πu:S.Quu.u \mapsto h\,u\,u \;:\; \Pi u{:}S.\, Q\,u\,u.

En otras palabras, en la función propiedad la palabra clave fun define la función que transforma la hipótesis general «QQ vale para todos los pares» en la versión específica «QQ vale para (u,u)(u, u)». Se le puede pedir a Lean que verifique el tipo de la definición, confirmando que coincide exactamente con la propiedad que se quería demostrar:

#check propiedad
-- Lean responde:
-- propiedad {S : Type} (Q : S -> S -> Prop)
-- : (forall (x y : S), Q x y) -> forall (u : S), Q u u

También se podría haber usado la notación de teorema, que simplifica la escritura y se explorará más adelante:

theorem propiedad {S : Type} (Q : S → S → Prop) :
(forall x y, Q x y) → forall u, Q u u :=
by
intro h u
exact h u u

Este ejemplo es la esencia de λP\lambda P: el tipo de la prueba Πu:S.Quu\Pi u{:}S.\, Q\,u\,u depende del término uu. La prueba misma es una función que, dado un término uu, produce una demostración específica de QuuQ\,u\,u. Esto materializa la capacidad de los tipos de depender de los términos, que es la contribución fundamental de este sistema.

Con las tres dependencias sobre la mesa (λ2\lambda 2, λω\lambda\underline{\omega} y λP\lambda P), la página siguiente las organiza en una sola figura: el cubo de Barendregt.