[Teoría lógica y computacional] Tipos de cálculo Lambda y modelado de cálculo Lambda

Tipos de cálculo Lambda


Hemos dominado intuicionista Lógica (IL),

-------------------------------------------------- ----------------------------------
PD: Este breve párrafo es la descripción ampliada del autor: lógica intuitiva . Como todos sabemos, la "lógica clásica / lógica de primer orden" más famosa es un punto de partida para aprender lógica, y otras lógicas sirven como puntos de referencia para referencia. La lógica clásica se centra en el "valor de verdad", y el "valor de verdad" declarado es su característica "absoluta". Una declaración inequívoca y bien formada es verdadera o falsa. Falso significa no verdadero, verdadero significa no falso, es la " ley del medio excluido ". Enfatiza la ley del medio excluido. Por ejemplo: Basándonos en la lógica clásica, podemos probar una proposición "no constructivamente". Como se muestra abajo:

                                 
Aunque la demostración anterior no tiene ningún problema en la lógica clásica, todavía no podemos estar seguros de qué caso es correcto. Además, también podemos hacer una prueba constructiva: para  x = \ sqrt2 \, y = 2 log_2 \ 3, nosotros  x ^ y = 3 \ en Q. Este método de razonamiento "constructivo" corresponde a la "lógica intuicionista". La base filosófica de la lógica intuicionista es que no existe una verdad absoluta, solo el conocimiento y la construcción intuicionista de un matemático idealizado (sujeto creativo). El juicio lógico es verdadero si y solo si la creación del tema puede verificarlo. Por tanto, la lógica intuicionista no acepta la ley del medio excluido .

El lenguaje del cálculo proposicional intuicionista (IPC) es el mismo que el de la lógica proposicional clásica. La semántica en la lógica intuicionista no se juzga mediante tablas de verdad, sino que se explica mediante la construcción de patrones. Esta es la famosa interpretación de BHK (interpretación de Brouwer-Heyting-Kolmogorov). Heyting es alumno de Brouwer. Kolmogorov tiene un estudiante famoso llamado Martin-Löf. Como sigue:   

                                
Hay muchas fórmulas lógicas que son tautológicas en la lógica clásica , pero no están construidas en la lógica intuicionista. Como el derecho común del medio excluido, la eliminación de la doble negación, la ley de De Morgan, la ley de la contradicción, etc. Un sistema de prueba de lógica intuicionista es el sistema de deducción natural   , donde J significa lógica intuicionista. NK es un sistema de deducción natural de lógica clásica.

El álgebra booleana es una interpretación semántica de la lógica clásica, mientras que el álgebra de Heiting es la semántica algebraica de la lógica intuicionista. En la lógica intuicionista, nuestra primera preocupación es la "demostrabilidad" más que el "valor de verdad". Para obtener más detalles, además de los enlaces anteriores, también puede consultar: lógica intuicionista

-------------------------------------------------- ----------------------------------

Volvamos al cálculo lambda: tenemos las herramientas lógicas que necesitamos para definir el modelo. Por supuesto, no hay nada más sencillo, ¿verdad?

Hasta ahora hemos discutido el cálculo lambda simple sin tipo. Igual que Church propuso la primera versión de LC por primera vez. Pero tiene algunos problemas. Para resolver estos problemas, la gente introdujo el concepto de "tipo" (tipo), por lo que apareció el cálculo lambda de tipo simple, y luego varias variantes: SystemT, SystemF, Lambda cube (y time cube no Cuál es la relación :-)) Espera. Al final, la gente se dio cuenta de que el cálculo lambda no tipificado es en realidad un caso especial mórbidamente simple de cálculo lambda tipificado: solo hay un tipo de LC.

La semántica del cálculo lambda es la más fácil de entender en el cálculo escrito. Ahora, echemos un vistazo al LC escrito más simple, llamado "cálculo lambda simplemente escrito" (cálculo lambda simplemente escrito), y cómo es semánticamente equivalente a la lógica intuitiva. (En realidad, cada tipo de LC corresponde a un tipo de IL, y la convención beta en cada LC corresponde a un razonamiento de un paso en IL, por lo que primero debemos correr para introducir la lógica intuitiva y luego volver a Aquí. )

El principal cambio en el cálculo lambda tipificado es la adición de un concepto llamado "tipos base". En el cálculo lambda tipificado, puede utilizar un universo de valores atómicos, que se dividen en diferentes tipos simples. El tipo base generalmente se nombra con una sola letra griega minúscula, pero este es el punto delicado de Blogger (el texto html normal no puede escribir letras griegas), tuve que usar letras mayúsculas en inglés en lugar del nombre del tipo. Entonces, por ejemplo, podemos tener un tipo "N", que contiene un conjunto de números naturales, o un tipo "B", correspondiente al valor booleano verdadero / falso, y una clase "S" correspondiente al tipo de cadena.

Ahora que tenemos los tipos básicos, analicemos los tipos de funciones. La función asigna el valor de un tipo (el tipo del parámetro) al valor del segundo tipo (el tipo del valor de retorno). Para una función que acepta un parámetro de entrada de tipo A y devuelve un valor de tipo B, escribimos su tipo como A -> B. "->" se llama un constructor de tipo de función, está asociado a la derecha, por lo que A -> B -> C significa A -> (B -> C).

Para aplicar tipos al cálculo lambda, tenemos que hacer algunas cosas. Primero, necesitamos actualizar la sintaxis para que podamos incluir información de tipo. En segundo lugar, necesitamos agregar un conjunto de reglas para indicar qué procedimientos escritos son legales.

La parte de gramática es muy sencilla. Agregamos un símbolo ":", el lado izquierdo de los dos puntos es el enlace de la expresión o variable, y el lado derecho es la especificación de tipo. Muestra que su lado izquierdo tiene el tipo especificado en su lado derecho. Para dar algunos ejemplos:

  • lambda x : N . x + 3. Indica que el x tipo de parámetro es N , es decir , un número natural. Aquí no se especifica el tipo del resultado de la función; pero sabemos que el tipo de la función "+" es  N -> N , por lo que se puede inferir que el tipo del resultado de la función es N.
  • (lambda x . x + 3) : N -> N, Que es el mismo que el anterior, pero se menciona la declaración de tipo, por lo que da el tipo de la expresión lambda como un todo. Esta vez podemos deducir  x : N porque el tipo de función es  N -> N, lo que significa que el tipo de parámetro de función es N.
  • lambda x : N, y : B . if y then x * x else x. Esta es una función de dos parámetros, el primer tipo de parámetro es N y el segundo tipo de parámetro es B. Podemos inferir que el tipo de retorno es N. Entonces, el tipo de la función completa es  N -> B -> N . Puede parecer extraño al principio; pero recuerde, el cálculo lambda en realidad solo tiene un único parámetro; la escritura de funciones multiparamétricas es solo una forma abreviada de curry. Entonces, de hecho, esta función es lambda x : N . (lambda y : B . if y then x * x else x):; el tipo de lambda interno es B -> N ; el tipo de  capa externa es  N -> (B -> N).

Para discutir si el programa está tipificado legalmente (es decir, "bien tipificado"), necesitamos introducir un conjunto de reglas de inferencia de tipos. Cuando usamos estas reglas para inferir el tipo de una expresión, lo llamamos juicio de tipo. La inferencia de tipo y el juicio nos permiten inferir el tipo de expresión lambda; si alguna parte de la expresión es inconsistente con el resultado del juicio de tipo, la expresión es ilegal. (Una de las motivaciones de Church para comenzar a estudiar la LC tipificada fue distinguir entre valores "atómicos" y valores de "predicado". Trató de evitar la paradoja godeliana utilizando tipos para asegurarse de que los predicados no puedan operar sobre predicados).

Usaré un conjunto de símbolos menos formales para indicar el criterio de tipo; los símbolos estándar son demasiado difíciles de representar con el software que utilizo actualmente. Los símbolos comunes son un poco como fracciones; el numerador está formado por oraciones que sabemos que son verdaderas; el denominador es lo que podemos inferir del numerador. A menudo usamos un concepto llamado "contexto" en la molécula, que contiene un conjunto de oraciones que sabemos que son verdaderas, generalmente expresadas como una letra griega mayúscula. Aquí utilizo letras griegas mayúsculas para indicarlo. Si un contexto de tipo contiene una declaración " x : A, lo escribiré  CONTEXT |- x : A. Para el símbolo de inferencia en forma de fracción, utilizo dos líneas para representarlo, la línea del numerador está marcada con" Dado: "y la línea del denominador está marcada con "Inferir:". (Para el uso normal de símbolos, visite la página STLC en Wikipedia ).

** Regla 1: (Identificación de tipo) **

Given: nothing 
Infer: x : A |- x : A 

La regla más simple: si solo conocemos la declaración de tipo de una variable, entonces sabemos que la variable es el tipo que declara.

** Regla 2: (tipo invariante) **

Given: GAMMA |- x : A, x != y 
Infer: (GAMMA + y : B) |- x : A 

Esta es una sentencia de no injerencia. Si lo sabemos  x : A, podemos inferir que cualquier otro tipo de juicio no puede cambiar nuestra xinferencia de tipo.

Regla 3: (inferencia de parámetros a funciones)

Given: (GAMMA + x : A) |- y : B 
Infer: GAMMA |- (lambda x : A . y) : A -> B 

Esta declaración nos permite inferir el tipo de función: si conocemos el tipo del parámetro de la función y el tipo del  Avalor de retorno de la función  B , entonces podemos deducir el tipo de la función  A -> B .

Finalmente, Regla 4: (Razonamiento de la aplicación funcional)

Given: GAMMA |- x : A -> B, GAMMA |- y : A 
Infer: GAMMA |- (x y) : B 

Si conocemos el tipo de una función  A -> B y lo aplicamos a Aun valor de tipo, el resultado es B una expresión de tipo  .

Las reglas son estas cuatro. Si tenemos una expresión lambda y el tipo de juicio de cada elemento en la expresión es consistente, entonces la expresión está bien escrita. Si no es así, la expresión es ilegal.

Encontremos algo de emoción y describamos los tipos de combinadores de SKI. Estos son tipos incompletos: uso variables de tipo en lugar de tipos concretos. En los programas que realmente usan combinadores, puede encontrar tipos reales para reemplazar las variables de tipo. No se preocupe, usaré un ejemplo para ilustrar este punto.

  • ICombinación: (lambda x . x) : A -> A
  • KCombinación: (lambda x : A . ((lambda y : B . x) : B -> A)): A -> B -> A
  • SCombinación: (lambda x : A -> B-> C . (lambda y : A -> B . (lambda z : A . (x z : B -> C) (y z : B)))) : (A -> B -> C) -> (A -> B) -> C

Ahora, vamos a ver unos simples expresiones lambda cálculo: lambda x y . y x. Dado que no hay declaraciones o parámetros sobre el tipo, no podemos saber el tipo exacto. Sin embargo, sabemos que xdebe tener un tipo determinado, lo llamamos A; y sabemos que yes una función, que toma xcomo parámetro de aplicación, por lo que su tipo de parámetro es A, pero se desconoce su tipo de resultado. Por lo tanto, usando variables de tipo, tenemos  x : A, y : A -> B. Podemos determinar la A suma  mirando y analizando la expresión concreta completa B . Entonces, usemos x = 3y y = lambda a : N. a * a para calcular el tipo. Supongamos que nuestro contexto de tipo ya contiene el  * tipo " N -> N -> N".

  • (lambda x y . y x) 3 (lambda a : N . a * a)
  • 3 es un entero, por lo que es de tipo:  3 : N .
  • De acuerdo con la regla 4, podemos deducir que a * a el tipo de expresión  es  N, donde  a : N ( *el tipo N -> N -> N:), por lo tanto, por la regla 3, el tipo de expresión lambda es  N - > N . Entonces, nuestra expresión ahora se convierte en:(lambda x y . y x) (3 : N) (lambda a : N . (a * a) : N) : N -> N
  • Entonces, ahora sabemos que el parámetro de la primera lambda  x debe ser un  N tipo y  yun  N -> N tipo. Sabemos que de acuerdo con la Regla 4, una expresión del tipo de aplicación  y x debe ser  N , entonces, de acuerdo con el artículo 3, el tipo de expresión es:  N -> (N -> N) -> N .
  • Entonces, los tipos A y B aquí están ambos al final N.

Entonces, ahora tenemos un cálculo lambda simple. Es simple escribir porque hay pocas formas de tratar los tipos: la única forma de crear un nuevo tipo es a través del ->constructor ". Otro cálculo lambda tipificado incluye la capacidad de definir "tipos paramétricos", que representan tipos como funciones de diferentes tipos.

 

El capítulo final, Modelado de cálculo Lambda: ¡el programa es la prueba!


Ya hemos hablado de la lógica intuicionista y sus modelos; desde el cálculo Lambda sin tipo hasta el cálculo Lambda simple con tipo; finalmente, podemos ver el modelo de cálculo Lambda. Y esto es lo realmente interesante.

Primero considere los tipos en el cálculo Lambda de tipo simple. Cualquier forma que se pueda generar a partir de la siguiente gramática es un tipo de cálculo Lambda:

type ::= primitive | function | ( type ) 
primitive ::= A | B | C | D | ... 
function ::= type -> type 

Una de las trampas de esta gramática es que puede crear expresiones de un tipo, y son definiciones de tipo legal, pero no puede escribir una expresión única, completa y cerrada con ese tipo. (Una expresión cerrada es una expresión sin variables libres). Si un tipo de expresión tiene un tipo, decimos que la expresión "habita" ese tipo y el tipo es un tipo de residencia. Si no existe una expresión para el tipo de residencia, decimos que es "inhabitable".

Entonces, ¿cuál es la diferencia entre el tipo de residencia y el tipo de no residente?

La respuesta proviene de una teoría llamada "isomorfismo de Curry-Howard". Esta teoría propone que todo cálculo lambda tipificado tiene una lógica intuitiva correspondiente; las expresiones de tipo son residentes si y solo si el tipo es un teorema en la lógica correspondiente.

Mira el tipo primero  A -> A. Ahora, no pensamos en él  -> como un constructor de tipo de función, sino como una implicación lógica. A 蕴含 A Evidentemente, el teorema de la lógica intuicionista. Por tanto, el tipo  A -> A es residente.

Ven y vuelve a ver  A -> B . Este no es un teorema a menos que pueda demostrarse en un contexto determinado. Como tipo de función, esto representa un tipo de función que, sin ningún contexto, toma el tipo A como parámetro y devuelve un tipo B diferente. No puede hacer esto, debe haber un contexto que proporcione un valor de tipo B, para acceder a este contexto, debe haber alguna forma de permitir que la función acceda a su contexto: una variable libre. Esto es lógicamente lo mismo que el cálculo lambda: necesita algún tipo de contexto para establecerlo  A->B como un teorema (lógicamente) o de tipo habitable (en cálculo lambda).

Será más fácil de entender a continuación. Si hay una expresión LC cerrada cuyo tipo es un teorema en la lógica intuicionista correspondiente, entonces el tipo de expresión es una prueba del teorema. Cada protocolo Beta es equivalente a un paso de razonamiento en lógica. La lógica correspondiente a este cálculo lambda es su modelo. En cierto sentido, el cálculo lambda y la lógica intuitiva son solo reflejos diferentes de la misma cosa.

Hay dos formas de probar este isomorfismo: una es el método de cálculo combinador que Currie usó originalmente, la otra es el llamado "cálculo secuencial" (cálculo secuencial). Combinaré la versión de la subprueba, por lo que la revisaré rápidamente a continuación. En el futuro, es probable que la próxima semana hable de la versión de cálculo sucesivo.

Recordemos qué es un modelo. Un modelo es una forma de indicar que cada enunciado en el cálculo es legal en algún rango específico, por lo que existe una relación correspondiente entre la entidad específica y la entidad en el cálculo, y todos los enunciados en el cálculo corresponden a la entidad real. declaraciones. Entonces, en realidad, no necesitamos ser completamente isomórficos; solo necesitamos un homomorfismo del cálculo a la lógica. (El isomorfismo es bidireccional, del cálculo a la lógica y de la lógica al cálculo; mientras que el homomorfismo solo del cálculo a la lógica).

Entonces, lo que tenemos que hacer es tomar cualquier expresión de cálculo lambda completa y luego transformarla en una serie de declaraciones lógicas intuitivas legales. Dado que la lógica intuitiva en sí misma ha demostrado ser legal, si podemos traducir el cálculo lambda a IL, entonces probaremos la legalidad del cálculo lambda, lo que significa que mostraremos que los cálculos en el cálculo lambda son cálculos legales, y el cálculo lambda es un Sistema informático completo, legal y eficaz.

¿Cómo obtenemos la lógica intuitiva de los combinadores (son solo una abreviatura del cálculo lambda con variables omitidas)? En realidad, es increíblemente simple.

Todas las pruebas en lógica intuicionista se pueden resumir en una serie de pasos, cada uno de los cuales es un razonamiento que utiliza uno de los siguientes dos axiomas básicos:

  • A implies B implies A
  • (A implies B implies C) implies ((A implies B) implies (A implies C))

Vamos a reescribirlos con flechas para que parezcan un tipo A -> B -> A :; y (A -> B -> C) -> ((A -> B) -> (A -> C)).

¿Familiar? Si no está familiarizado, mire hacia atrás en el cálculo lambda simple escrito . Este es el tipo de combinador S y K.

Los siguientes pasos del modelado son obvios. El tipo de cálculo lambda corresponde al tipo atómico de lógica intuitiva. Las funciones son reglas de inferencia. Cada función se puede reducir a una expresión de combinador; cada expresión de combinador es una instancia de alguna regla de razonamiento básica de la lógica intuicionista. Por tanto, la función se convierte en una prueba constructiva del teorema en la lógica correspondiente.

¿Guay, verdad?

(Cualquier persona normal dirá "¿qué?" Después de leerlo, pero obviamente no soy una persona normal, soy un fanático de las matemáticas).

Supongo que te gusta

Origin blog.csdn.net/smilejiasmile/article/details/107829573
Recomendado
Clasificación