No puedo hacer nada mejor que la respuesta general de Sridhar Ramesh , pero creo que algunas cositas podrían ayudar con los detalles de la pregunta.
Dado que el isomorfismo relaciona las pruebas con los términos y las proposiciones con los tipos, verificar una prueba es como verificar un término. Para la mayoría de los sistemas tipográficos que probablemente nos interesen, eso es decidible.
No es tanto que el isomorfismo se aplique solo a programas que se detienen, sino que los lenguajes sin terminación corresponden a sistemas a prueba de sonido. Para ver por qué es así, considere el esquema de tipo del combinador Y en el cálculo lambda de tipo simple:
[matemáticas] (A \ a {A}) \ a {A} [/ matemáticas]
- ¿Cómo se traduciría a lógica la frase en inglés “no haga A y B”?
- ¿Te parece más claro en Lojban?
- ¿Qué es la hipercomputación y cuáles son los conceptos involucrados en ella?
- ¿Cómo deriva un programador la lógica de cualquier programa?
- ¿Qué son los árboles de prueba?
Si tiene Y, entonces, para cualquier proposición [matemática] A [/ matemática], puede obtener una prueba aplicando Y a la función de identidad en ese tipo. Por lo tanto, agregar el combinador Y a STLC lo hace poco lógico para la lógica intuicionista. (Si STLC no está en tu callejón, podemos hacer más o menos lo mismo en, por ejemplo, Java. Ver más abajo). Los lenguajes que siempre terminan son triviales desde algunas perspectivas, pero no necesariamente desde otros. En realidad, hay mucho que puedes hacer con la recursividad primitiva, y muchos lenguajes completos de Turing incluyen fragmentos de normalización que aún son interesantes.
“Ejecutar” una prueba es la normalización de la prueba. En particular, la regla beta estándar del cálculo lambda corresponde a la eliminación del corte:
[matemáticas] \ frac {\ frac {\ Delta, x: A \ vdash {M: B}} {\ Delta \ vdash \ lambda {x} .M: A \ to {B}} \ quad \ Delta \ vdash { N: A}} {\ Delta \ vdash (\ lambda {x} .M) \, N: B} \ longmapsto {\ Delta \ vdash {M [N / x]: B}} [/ math]
Esto significa que los sistemas de prueba con algoritmos de eliminación de cortes corresponden a lenguajes de programación de normalización (terminación).
Creo que Sridhar cubrió la mayor parte de por qué es importante. Dos razones más:
- En un lenguaje de programación mecanografiado con tipos suficientemente expresivos, los tipos se pueden usar para establecer teoremas sobre programas, y las pruebas de los teoremas son en sí mismos programas. La verificación de la prueba es realmente una verificación de tipo. Esta es la idea detrás de los asistentes de pruebas basados en la teoría de tipos como Coq, Agda y Twelf.
- La lógica puede inspirar nuevos sistemas de tipos para lenguajes de programación. Por ejemplo, algunas personas se han preguntado acerca de las interpretaciones de Curry-Howard de las lógicas modales. Es decir, ¿qué lenguaje de programación y sistema de tipos podría corresponder a una lógica modal dada? Algunas personas han presentado respuestas interesantes a esto, por ejemplo, utilizando el operador modal para indicar recursos remotos en un idioma para programas distribuidos. Del mismo modo, la lógica lineal ha inspirado el trabajo en los sistemas de tipos conscientes de los recursos.
Ahora para el bit prometido de Java. Podemos construir un sistema de prueba (¡poco sólido!) En Java. Usaremos tipos Java para representar proposiciones en lógica intuicionista, y construir un valor Java (o clase) con ese tipo es una prueba de la proposición.
A continuación, declaro las clases
para representar la proposición [math] \ top [/ math] y True
para representar la proposición [math] \ bot [/ math]. El primero puede ser instanciado (dando una prueba de [math] \ top [/ math]), pero el último no. La interfaz False
representa la implicación [math] A \ to {B} [/ math]. Debajo de eso, doy varias clases correspondientes a los teoremas de la lógica intuicionista, y una clase que no debería ser un teorema, pero es demostrable en Java usando la recursividad. (Todos estos son demostrables usando Impl
también.) No quiero decir que sea una buena demostración del isomorfismo de Curry-Howard, pero podría ayudar a demostrar por qué la no terminación conduce a la falta de solidez. null
class CurryHoward { // Truth public class True { }; // Falsity (no instances) public class False { private False () { } }; // Implication public interface Impl { B pf (A a); }; // A -> A public class I implements Impl { public A pf (A a) { return a; } }; // A -> B -> A public class K implements Impl<A, Impl> { public Impl pf (final A a) { return new Impl() { public A pf (B b) { return a; } }; } } // (A -> B -> C) -> (A -> B) -> (A -> C) public class S implements Impl<Impl<A, Impl>, Impl<Impl, Impl>> { public Impl<Impl, Impl> pf (final Impl<A, Impl> f) { return new Impl<Impl, Impl>() { public Impl pf (final Impl g) { return new Impl() { public C pf (A a) { return f.pf(a).pf(g.pf(a)); } }; } }; } } // recursion leads to unsoundness: // A -> B public class AtoB implements Impl { public B pf(A a) { return pf(a); } } };