Nivel 6 · El CIC y Lean

Tipos inductivos

El CIC es una extensión del CoC que resuelve los problemas de la página anterior añadiendo una herramienta fundamental: la capacidad de definir tipos inductivos de forma nativa. En lugar de codificar los números naturales, simplemente se los declara especificando sus reglas de construcción: un conjunto nat\texttt{nat} con dos constructores,

  • un constructor para el cero: 0:nat0 : \texttt{nat}, y
  • una función sucesor: S:natnatS : \texttt{nat} \to \texttt{nat}.

Esta simpleza le da al sistema toda la información que necesita. El CIC entiende que un natural es, o bien 00, o bien el SS de otro natural, y nada más. A partir de esta definición, el sistema genera automáticamente las herramientas de razonamiento y cómputo asociadas (la página siguiente las estudia en detalle). El desarrollo formal sigue el artículo de Paulin-Mohring Paulin-Mohring (1993) .

De la intuición al formalismo: gramática y convenciones

Para enunciar las reglas con precisión se adopta la notación de los Sistemas de Tipos Generalizados (GTS), también llamados Pure Type Systems (PTS) (Paulin-Mohring, 1993) . La sintaxis abstracta de términos es

t::=sx(x:t)t[x:t]t(t t)t ::= s \mid x \mid (x{:}t)\, t \mid [x{:}t]\, t \mid (t\ t)

donde ss recorre el conjunto de sorts. Se usan las convenciones estándar:

  • Producto dependiente: (x:A)B(x{:}A)\,B denota Πx:A.B\Pi x{:}A.\,B.
  • Abstracción: [x:A]t[x{:}A]\,t denota λx:A.t\lambda x{:}A.\,t.
  • Flecha como caso particular: si xFV(B)x \notin FV(B), se escribe AB(x:A)BA \to B \equiv (x{:}A)\,B.
  • Asociatividad: la flecha asocia a la derecha, la aplicación a la izquierda.
  • Sustitución: M[x:=N]M[x := N] es la sustitución de NN por las ocurrencias libres de xx en MM.

El símbolo ::=::= proviene de la notación BNF y define reglas de producción sintáctica; no debe confundirse con la igualdad matemática. Un tratamiento detallado de los GTS puede encontrarse en Barendregt (1991) . La página sobre universos volverá sobre las sorts; por ahora basta leer Set\text{Set} como «la sort de los tipos de datos».

Aridades y formas de constructor

Definición (Aridad)

Una aridad de una sort ss describe la forma del tipo inductivo que se va a definir. Se genera por la gramática

Ar::=s(x:M)Ar.Ar ::= s \mid (x : M)\,Ar.

Por ejemplo, la aridad de nat\texttt{nat} es Set\text{Set}, mientras que la de las listas (parametrizadas por un tipo) es (A:Set)Set(A{:}\,\text{Set})\,\text{Set} (Paulin-Mohring, 1993) .

Lean no usa la palabra aridad; su documentación describe la firma del constructor de tipo como una secuencia de argumentos (un «telescopio»): primero los parámetros, después los índices, y al final la sort de retorno. En particular, List tiene firma (α:Type u)Type u(\alpha{:}\,\texttt{Type}\ u)\,\texttt{Type}\ u y Fin tiene (n:N)Type(n{:}\,\mathbb{N})\,\texttt{Type}:

#check List -- : Type u → Type u -- parámetro α
#check Fin -- : Nat → Type -- parámetro n
Nota (Parámetros frente a índices)

La firma externa Nat → Type no distingue entre parámetro e índice; la diferencia está en la definición. Un parámetro aparece uniformemente a la izquierda de los dos puntos en toda la declaración: en Lean 4, el argumento nn de Fin es un parámetro. Un índice genuino, en cambio, es fijado por cada constructor en su conclusión: el ejemplo canónico es el segundo argumento de Eq : α → α → Prop, que el constructor Eq.refl a instancia en a. Los parámetros son constantes a lo largo de la definición; los índices varían con cada constructor.

Definición (Tipo estrictamente positivo)

Sea AA una aridad y XX una variable de tipo AA. Un tipo PP es estrictamente positivo con respecto a XX si XX no aparece a la izquierda de ninguna flecha \to, a ninguna profundidad de anidamiento. Un tipo estrictamente positivo bien formado puede escribirse como (x:M)(X m)(\vec{x} : \vec{M})(X\ \vec{m}), con la restricción de que XX no aparece libremente en ningún término de M\vec{M} ni de m\vec{m}.

La notación vectorial denota secuencias finitas, posiblemente vacías: (x:M)(\vec{x} : \vec{M}) abrevia (x1:M1)(xn:Mn)(x_1 : M_1)\dots(x_n : M_n), y (X m)(X\ \vec{m}) es la aplicación de XX a argumentos m1mkm_1 \dots m_k, cuya longitud viene dada por la aridad de XX.

Nota (Positividad estricta y consistencia)

La condición es estrictamente más fuerte que prohibir las posiciones contravariantes: en (XA)B(X \to A) \to B la ocurrencia de XX es covariante (dos inversiones) y, sin embargo, el tipo no es estrictamente positivo; en presencia de impredicatividad, admitir tales tipos también compromete la consistencia. La razón de fondo es la siguiente: si se admitiera una ocurrencia negativa, por ejemplo un constructor de tipo (X)X(X \to \bot) \to X, se podría fabricar un punto fijo y, con él, un habitante de \bot, reproduciendo dentro del sistema de tipos la paradoja de Russell. La positividad estricta es la barrera que lo impide.

Definición (Forma de constructor)

Sea AA una aridad y XX una variable de tipo AA. Un término CC es una forma de constructor con respecto a XX si es un tipo cuya conclusión es XX y donde todas las ocurrencias de XX en los tipos de sus argumentos son estrictamente positivas.

Por ejemplo, en la definición de los naturales, la aridad es Set\text{Set} y XX una variable de tipo Set\text{Set}; los tipos de los constructores son XX (para 00) y XXX \to X (para SS). Ambos son formas de constructor válidas. Los constructores realizan la introducción del tipo inductivo. En Lean:

#check Nat.zero -- Nat
#check Nat.succ -- Nat → Nat
#check List.nil -- List α
#check List.cons -- α → List α → List α

Las reglas de formación e introducción

Se introducen nuevos pseudo-términos para representar las definiciones inductivas y sus constructores. Conviene subrayar que Ind\text{Ind}, Constr\text{Constr} y Elim\text{Elim} son pseudo-términos de Paulin-Mohring (Paulin-Mohring, 1993) para describir la maquinaria interna del núcleo; no son sintaxis de Lean: allí se realizan con la palabra clave inductive (formación), los nombres de los constructores (introducción) y el eliminador .rec que el sistema genera.

Formación (Ind). Se introduce el constructor de términos Ind(X:A) {C1Cn}\text{Ind}(X{:}A)\ \lbrace C_1 \mid \dots \mid C_n \rbrace. Si AA es una aridad de ss y cada CiC_i es una forma de constructor de XX bien tipada en la sort ss (bajo el supuesto X:AX : A), el tipo inductivo está bien formado y tiene tipo AA:

arity(A,s)(i=1n)  Γ,X:ACi:sconstructor(Ci,X)ΓInd(X:A){C1Cn}:A(Ind)\frac{\text{arity}(A, s) \qquad (\forall i = 1 \dots n)\ \ \Gamma,\, X{:}A \vdash C_i : s \qquad \text{constructor}(C_i, X)}{\Gamma \vdash \text{Ind}(X{:}A)\lbrace C_1 \mid \ldots \mid C_n \rbrace : A} \quad (\text{Ind})

El símbolo \mid actúa como separador sintáctico de la lista de constructores; semánticamente denota que el tipo inductivo es una unión disjunta generada por estas reglas: un término habita el tipo si fue construido mediante C1C_1 o C2C_2o CnC_n.

Ejemplo 10.4 (Números naturales)

Para definir el tipo de los números naturales se utiliza la aridad A=SetA = \text{Set}. La variable XX representa al propio tipo nat\texttt{nat} dentro de la definición.

  • El constructor del cero tiene tipo nat\texttt{nat}, que se escribe formalmente como XX.
  • El constructor del sucesor tiene tipo natnat\texttt{nat} \to \texttt{nat}, que se escribe formalmente como XXX \to X.

Por lo tanto, la definición formal es

natInd(X:Set){XXX}.\texttt{nat} \equiv \text{Ind}(X{:}\,\text{Set})\lbrace X \mid X \to X \rbrace.

Esto ilustra cómo el separador \mid distingue entre el caso base (cero) y el caso recursivo (sucesor).

Introducción (Constr). Para cada tipo inductivo I:=Ind(X:A){C1Cn}I := \text{Ind}(X{:}A)\lbrace C_1 \mid \dots \mid C_n \rbrace se introducen términos Constr(i,I)\text{Constr}(i, I), con 1in1 \le i \le n. La regla de tipado asigna a cada constructor el tipo de su especificación, sustituyendo la variable abstracta XX por el propio tipo inductivo II:

ΓI:A1inΓConstr(i,I):Ci[X:=I](Constr)\frac{\Gamma \vdash I : A \qquad 1 \le i \le n}{\Gamma \vdash \text{Constr}(i, I) : C_i[X := I]} \quad (\text{Constr})

En Lean, la declaración correspondiente es directa y el paralelismo con el formalismo es exacto:

inductive Nat where
| zero : Nat
| succ : Nat → Nat
inductive List (α : Type u) where
| nil : List α
| cons : α → List α → List α

Con la formación y la introducción en la mano, falta la pieza que dota al sistema de poder lógico y computacional: el principio de eliminación, tema de la página siguiente.