Nivel 6 · El CIC y Lean

Recursores y eliminación

La consecuencia más profunda de definir un tipo por sus constructores es que el sistema puede generar automáticamente su principio de eliminación, también conocido como recursor . Esta página lo presenta primero en el caso familiar de N\mathbb{N}, luego en su forma esquemática general, y cierra con la regla de cómputo que lo hace efectivo: la ι\iota-reducción.

El recursor de los naturales

Para los números naturales, el recursor permite definir funciones por recursión primitiva. El artículo de Paulin-Mohring (Paulin-Mohring, 1993) lo denota R(C,x,f,n)R(C, x, f, n), con las siguientes reglas de cómputo:

  • R(C,x,f,0)R(C, x, f, 0) se comporta como xx;
  • R(C,x,f,(S n))R(C, x, f, (S\ n)) se comporta como (f n R(C,x,f,n))(f\ n\ R(C, x, f, n));

donde CC es el tipo de retorno, xx el valor devuelto en el caso base, ff la función del paso inductivo (que calcula el resultado para S nS\ n a partir de nn y de la llamada recursiva) y nn el argumento sobre el cual se elimina.

Este recursor se generaliza a un principio de inducción completo, la eliminación dependiente. El eliminador unificado es una función cuyo tipo es el principio de inducción matemática:

P:natProp, (P 0)(u:nat, (P u)(P (S u)))n:nat, (P n)\forall P : \texttt{nat} \to \text{Prop},\ (P\ 0) \to (\forall u{:}\,\texttt{nat},\ (P\ u) \to (P\ (S\ u))) \to \forall n{:}\,\texttt{nat},\ (P\ n)

Leído componente a componente: para cualquier propiedad PP sobre los naturales, si se proporciona una prueba del caso base P 0P\ 0 y una prueba del paso inductivo (una función que, para cualquier uu, transforma una prueba de P(u)P(u) en una de P(S u)P(S\ u)), se obtiene una función que, para cualquier nn, construye una prueba de P(n)P(n). La inducción ya no es un principio inalcanzable sobre una codificación: es un principio de razonamiento generado automáticamente por la propia definición del tipo.

Nota 10.2 (Lo que un matemático ya conoce: teorema de recursión e inducción de Peano)

Vale la pena traducir el recursor a un lenguaje familiar, porque no introduce nada ajeno a la matemática ordinaria; solo lo vuelve primitivo. Las dos reglas de cómputo del recursor sobre N\mathbb{N}, a saber, R(C,x,f,0)=xR(C,x,f,0) = x y R(C,x,f,(Sn))=f n R(C,x,f,n)R(C,x,f,(S\,n)) = f\ n\ R(C,x,f,n), son exactamente las ecuaciones del teorema de recursión: dado un valor inicial xx y una regla de paso ff, existe una única función con g(0)=xg(0) = x y g(n+1)=f(n,g(n))g(n{+}1) = f(n, g(n)). Por ejemplo, el factorial es la instancia R(N, 1, (λnr. (Sn)r), n)R(\mathbb{N},\ 1,\ (\lambda\, n\, r.\ (S\,n)\cdot r),\ n). Y el tipo de la versión dependiente, P, P0(u, PuP(Su))n, Pn\forall P,\ P\,0 \to (\forall u,\ P\,u \to P\,(S\,u)) \to \forall n,\ P\,n, es, leído palabra por palabra, el principio de inducción de Peano. La diferencia con la matemática usual no es de contenido sino de estatus: donde Peano postula la inducción como axioma y la codificación impredicativa ni siquiera permite demostrarla (solo proporciona la recursión; la inducción dependiente no es derivable en el CoC puro (Geuvers, 2001) ), el CIC la genera junto con el tipo. Definir un tipo inductivo equivale, pues, a obtener de una vez su definición por constructores, su teorema de recursión y su principio de inducción.

Las reglas generales de eliminación

La regla de eliminación es la pieza central que dota de poder computacional y lógico a las definiciones inductivas. Se introduce un pseudo-término Elim(c,Q){f1fn}\text{Elim}(c, Q)\lbrace f_1 \mid \dots \mid f_n \rbrace que generaliza la recursión sobre los naturales a cualquier estructura inductiva. Se distinguen dos casos: eliminación no dependiente (para definir funciones) y dependiente (para pruebas por inducción).

Ejemplo 10.5 (El eliminador es la recursión de siempre (ℕ))

Antes de la regla general conviene fijar el caso de N\mathbb{N}, con constructores zero\texttt{zero} y succ\texttt{succ}. El eliminador no dependiente Elim(n,Q){f0fS}\text{Elim}(n, Q)\lbrace f_0 \mid f_S \rbrace recibe un valor f0:Qf_0 : Q para el caso zero\texttt{zero} y un manejador fSf_S para succ\texttt{succ}, y es la recursión primitiva:

Elim(zero,Q){f0fS}=f0,Elim(succ k,Q){f0fS}=fS k (Elim(k,Q){f0fS}).\text{Elim}(\texttt{zero}, Q)\lbrace f_0 \mid f_S \rbrace = f_0, \qquad \text{Elim}(\texttt{succ}\ k, Q)\lbrace f_0 \mid f_S \rbrace = f_S\ k\ \big(\text{Elim}(k, Q)\lbrace f_0 \mid f_S \rbrace\big).

La notación Ci{I,Q}C_i\lbrace I, Q \rbrace que aparece en las reglas no es más que el tipo de cada manejador tras reemplazar la ocurrencia recursiva por un resultado de tipo QQ: para zero\texttt{zero} es Czero{N,Q}=QC_{\texttt{zero}}\lbrace\mathbb{N}, Q\rbrace = Q; para succ\texttt{succ} es Csucc{N,Q}=NQQC_{\texttt{succ}}\lbrace\mathbb{N}, Q\rbrace = \mathbb{N} \to Q \to Q (el predecesor y el resultado de la llamada recursiva). En la versión dependiente, donde QQ depende del valor, el caso succ\texttt{succ} se vuelve Csucc{N,Q,succ}=(n:N)QnQ(succ n)C_{\texttt{succ}}\lbrace\mathbb{N}, Q, \texttt{succ}\rbrace = (n{:}\,\mathbb{N}) \to Q\,n \to Q\,(\texttt{succ}\ n), que es exactamente el paso inductivo de Peano. Las reglas generales que siguen solo expresan esto mismo para un tipo inductivo cualquiera.

Eliminación no dependiente. Se usa para definir funciones cuyo tipo de retorno no depende del valor de entrada. Sea I:=Ind(X:A){C1Cn}I := \text{Ind}(X{:}A)\lbrace C_1 \mid \dots \mid C_n \rbrace con A(x:A)sA \equiv (\vec{x}{:}\vec{A})\,s (el \equiv denota aquí descomposición estructural: AA se analiza como una secuencia de productos que termina en la sort ss). La regla de tipado es

Γc:(I a)ΓQ:(x:A)sΓfi:Ci{I,Q}  (i=1,,n)ΓElim(c,Q){f1fn}:(Q a)(Nodeps,s)\frac{\Gamma \vdash c : (I\ \vec{a}) \qquad \Gamma \vdash Q : (\vec{x}{:}\vec{A})\,s' \qquad \Gamma \vdash f_i : C_i\lbrace I, Q \rbrace\ \ (\forall i = 1,\dots,n)}{\Gamma \vdash \text{Elim}(c, Q)\lbrace f_1 \mid \ldots \mid f_n \rbrace : (Q\ \vec{a})} \quad (\text{Nodep}_{s,s'})

Aquí QQ es el tipo de retorno y cada fif_i maneja el caso del constructor ii-ésimo; Ci{I,Q}C_i\lbrace I, Q \rbrace es la transformación del tipo del constructor para incluir las llamadas recursivas, que ahora devuelven un resultado de tipo QQ.

Eliminación dependiente. Es la forma más general y corresponde al principio de inducción estructural: el tipo de retorno QQ puede depender no solo de los parámetros del tipo inductivo, sino también del valor inductivo mismo.

Γc:(I a)ΓQ:(x:A)(I x)sΓfi:Ci{I,Q,Constr(i,I)}  (i=1,,n)ΓElim(c,Q){f1fn}:(Q a c)(Deps,s)\frac{\Gamma \vdash c : (I\ \vec{a}) \qquad \Gamma \vdash Q : (\vec{x}{:}\vec{A})(I\ \vec{x}) \to s' \qquad \Gamma \vdash f_i : C_i\lbrace I, Q, \text{Constr}(i, I) \rbrace\ \ (\forall i = 1,\dots,n)}{\Gamma \vdash \text{Elim}(c, Q)\lbrace f_1 \mid \ldots \mid f_n \rbrace : (Q\ \vec{a}\ c)} \quad (\text{Dep}_{s,s'})

La notación Ci{I,Q,Constr(i,I)}C_i\lbrace I, Q, \text{Constr}(i, I) \rbrace representa el tipo del caso ii-ésimo, donde las hipótesis inductivas tienen ahora el tipo dependiente QQ aplicado a los subtérminos recursivos.

Nota 10.6 (Restricciones sobre los pares (s, s'))

Las reglas anteriores no valen para pares (s,s)(s, s') arbitrarios. La eliminación fuerte (hacia Type\text{Type}) se restringe a tipos inductivos pequeños, y un inductivo que habita Prop\text{Prop} solo puede eliminarse hacia Prop\text{Prop}, salvo en los casos vacío y unitario (eliminación singleton). Sin estas restricciones el sistema sería inconsistente (eliminación fuerte sobre una sort impredicativa) y las pruebas en Prop\text{Prop} dejarían de ser borrables, contra la irrelevancia de pruebas que la página sobre universos explica en detalle (Paulin-Mohring, 1993) .

La reducción iota

El comportamiento computacional del eliminador se define mediante una nueva regla de reducción, la reducción-ι\iota, que estipula cómo se reduce una aplicación de Elim\text{Elim} a un término construido con Constr\text{Constr}:

Elim((Constr(i,I) m),Q){f}ι(Ci[I, Fun_Elim(I,Q,f), fi] m)\text{Elim}((\text{Constr}(i, I)\ \vec{m}), Q)\lbrace \vec{f} \rbrace \longrightarrow_{\iota} \left( C_i\left[ I,\ \text{Fun\_Elim}(I, Q, \vec{f}),\ f_i \right]\ \vec{m} \right)

La expresión del lado derecho es compleja, pero su intuición es simple: se selecciona la función fif_i correspondiente al constructor ii y se le aplican los argumentos del constructor m\vec{m}; la parte Ci[]C_i[\dots] se encarga de sustituir las llamadas recursivas por aplicaciones del propio término de eliminación, encapsulado en

Fun_Elim(I,Q,f)[x:A][c:(I x)] Elim(c,Q){f},\text{Fun\_Elim}(I, Q, \vec{f}) \equiv [\vec{x}{:}\vec{A}]\,[c{:}(I\ \vec{x})]\ \text{Elim}(c, Q)\lbrace \vec{f} \rbrace,

que actúa como el manejador de las llamadas recursivas dentro de la estructura del término. Para los naturales, esto instancia las conocidas reglas de reducción de la recursión:

Elim(zero,Q){f0fS}ιf0Elim((S n),Q){f0fS}ι(fS n Elim(n,Q){f0fS})\begin{aligned} \text{Elim}(\texttt{zero}, Q)\lbrace f_0 \mid f_S \rbrace &\longrightarrow_{\iota} f_0 \\ \text{Elim}((S\ n), Q)\lbrace f_0 \mid f_S \rbrace &\longrightarrow_{\iota} (f_S\ n\ \text{Elim}(n, Q)\lbrace f_0 \mid f_S \rbrace) \end{aligned}

En Lean, la igualdad por definición incluye la regla ι\iota (un recursor aplicado a un constructor se reduce por definición) y todo inductive trae su recursor T.rec. De ahí que, para Nat.rec, las igualdades siguientes se cierren con rfl:

example (Q : Nat → Sort u) (z : Q 0)
(s : (n : Nat) → Q n → Q (n+1)) :
Nat.rec z s 0 = z := rfl
example (Q : Nat → Sort u) (z : Q 0)
(s : (n : Nat) → Q n → Q (n+1)) (n : Nat) :
Nat.rec z s (Nat.succ n) = s n (Nat.rec z s n) := rfl

La regla ι\iota computa el recursor: en 00 devuelve la base zz y en SnS\,n aplica el paso ss al resultado recursivo; como ambas son reducciones por definición, se cierran con rfl.

Nota (A quién pertenecen las reglas iota)

Las reglas de ι\iota-reducción se generan para el tipo inductivo: gobiernan a Nat.rec aplicado a constructores. Las ecuaciones de una definición recursiva como Nat.add (la suma, notación +, definida internamente mediante Nat.rec) no son reglas primitivas nuevas: el núcleo las valida desplegando la definición (reducción δ\delta) y aplicando β\beta e ι\iota sobre Nat.rec. Así, que Nat.add m 0 sea definicionalmente igual a m es un teorema del cómputo δ+β+ι\delta + \beta + \iota, no un axioma adicional.

Nota 10.7 (Anatomía de un tipo inductivo)

Conviene reunir las piezas vistas, que son siempre las mismas cuatro y aparecen generadas por una sola declaración:

  1. Formación (Ind): se declara el tipo enumerando sus constructores (por ejemplo, Nat con zero y succ).
  2. Introducción (Constr): los constructores son las únicas maneras de construir un habitante del tipo.
  3. Eliminación (Elim/recursor): la única manera de usar un habitante; en su forma no dependiente define funciones por recursión, y en la dependiente es el principio de inducción.
  4. Cómputo (ι\iota-reducción): fija cómo se reduce el eliminador frente a cada constructor, haciendo el cálculo efectivo y la verificación de tipos decidible.

Todo tipo inductivo de Lean (incluidas la sintaxis de fórmulas y la relación de derivabilidad del módulo 07) se entiende con este mismo molde.