Cálculo lambda - Lambda calculus

El cálculo lambda (también escrito como cálculo λ ) es un sistema formal en lógica matemática para expresar el cálculo basado en la abstracción y aplicación de funciones mediante la vinculación y sustitución de variables . Es un modelo de cálculo universal que se puede utilizar para simular cualquier máquina de Turing . Fue introducido por el matemático Alonzo Church en la década de 1930 como parte de su investigación sobre los fundamentos de las matemáticas .

El cálculo lambda consiste en construir términos lambda y realizar operaciones de reducción en ellos. En la forma más simple de cálculo lambda, los términos se construyen usando solo las siguientes reglas:

Sintaxis Nombre Descripción
X Variable Carácter o cadena que representa un parámetro o valor matemático / lógico.
x . M ) Abstracción Definición de función ( M es un término lambda). La variable x se une a la expresión.
( M N ) Solicitud Aplicar una función a un argumento. M y N son términos lambda.

produciendo expresiones como: (λ xy . (λ z . (λ x . zx ) (λ y . zy )) ( xy )). Los paréntesis se pueden quitar si la expresión no es ambigua. Para algunas aplicaciones, se pueden incluir términos para operaciones y constantes lógicas y matemáticas.

Las operaciones de reducción incluyen:

Operación Nombre Descripción
x . M [ x ]) → (λ y . M [ y ]) α-conversión Cambiar el nombre de las variables vinculadas en la expresión. Se usa para evitar colisiones de nombres .
((λ x . M ) E ) → ( M [ x  : = E ]) β-reducción Reemplazo de las variables vinculadas con la expresión del argumento en el cuerpo de la abstracción.

Si se usa la indexación de De Bruijn , entonces la conversión α ya no es necesaria ya que no habrá colisiones de nombres. Si la aplicación repetida de los pasos de reducción finalmente termina, entonces, según el teorema de Church-Rosser , producirá una forma β-normal .

Los nombres de variable no son necesarios si se usa una función lambda universal, como Iota y Jot , que pueden crear cualquier comportamiento de función llamándola sobre sí misma en varias combinaciones.

Explicación y aplicaciones

El cálculo lambda es Turing completo , es decir, es un modelo universal de cálculo que se puede utilizar para simular cualquier máquina de Turing . Su homónimo, la letra griega lambda (λ), se usa en expresiones lambda y términos lambda para denotar la vinculación de una variable en una función .

El cálculo lambda puede no estar tipificado o tipificado . En el cálculo lambda tipado, las funciones se pueden aplicar solo si son capaces de aceptar el "tipo" de datos de la entrada dada. Los cálculos lambda tipificados son más débiles que el cálculo lambda no tipificado, que es el tema principal de este artículo, en el sentido de que los cálculos lambda tipificados pueden expresar menos que el cálculo lambda no tipificado, pero por otro lado los cálculos lambda tipificados permiten probar más cosas. ; en el cálculo lambda simplemente tipado es, por ejemplo, un teorema que toda estrategia de evaluación termina para cada término lambda simplemente tipado, mientras que la evaluación de términos lambda no tipados no necesita terminar. Una razón por la que hay muchos cálculos lambda con tipos diferentes ha sido el deseo de hacer más (de lo que puede hacer el cálculo sin tipo) sin renunciar a poder demostrar teoremas sólidos sobre el cálculo.

El cálculo lambda tiene aplicaciones en muchas áreas diferentes en matemáticas , filosofía , lingüística e informática . El cálculo lambda ha jugado un papel importante en el desarrollo de la teoría de los lenguajes de programación . Los lenguajes de programación funcional implementan el cálculo lambda. El cálculo lambda también es un tema de investigación actual en la teoría de categorías .

Historia

El cálculo lambda fue introducido por el matemático Alonzo Church en la década de 1930 como parte de una investigación sobre los fundamentos de las matemáticas . Se demostró que el sistema original era lógicamente inconsistente en 1935 cuando Stephen Kleene y JB Rosser desarrollaron la paradoja de Kleene-Rosser .

Posteriormente, en 1936, Church aisló y publicó solo la parte relevante para el cálculo, lo que ahora se denomina cálculo lambda no tipificado. En 1940, también introdujo un sistema computacionalmente más débil, pero lógicamente consistente, conocido como cálculo lambda simplemente tipado .

Hasta la década de 1960, cuando se aclaró su relación con los lenguajes de programación, el cálculo lambda era solo un formalismo. Gracias a las aplicaciones de Richard Montague y otros lingüistas en la semántica del lenguaje natural, el cálculo lambda ha comenzado a disfrutar de un lugar respetable tanto en lingüística como en informática.

Origen del símbolo lambda

Existe cierta incertidumbre sobre la razón por la que Church usa la letra griega lambda (λ) como la notación para la función-abstracción en el cálculo lambda, quizás en parte debido a las explicaciones contradictorias del propio Church. Según Cardone y Hindley (2006):

Por cierto, ¿por qué Church eligió la notación "λ"? En [una carta inédita de 1964 a Harald Dickson] afirmó claramente que provenía de la notación " " utilizada para la abstracción de clase por Whitehead y Russell , modificando primero " " a "∧ " para distinguir la abstracción de función de la abstracción de clase, y luego cambiar “∧” a “λ” para facilitar la impresión.

Este origen también se informó en [Rosser, 1984, p.338]. Por otro lado, en sus últimos años, Church les dijo a dos investigadores que la elección era más accidental: se necesitaba un símbolo y, por casualidad, se eligió λ.

Dana Scott también ha abordado esta cuestión en varias conferencias públicas. Scott relata que una vez le hizo una pregunta sobre el origen del símbolo lambda al yerno de Church, John Addison, quien luego le escribió una postal a su suegro:

Estimado profesor Church,

Russell tenía el operador iota , Hilbert tenía el operador épsilon . ¿Por qué eligió lambda para su operador?

Según Scott, toda la respuesta de Church consistió en devolver la postal con la siguiente anotación: " eeny, meeny, miny, moe ".

Descripción informal

Motivación

Las funciones computables son un concepto fundamental dentro de la informática y las matemáticas. El cálculo lambda proporciona una semántica simple para el cálculo, lo que permite estudiar formalmente las propiedades del cálculo. El cálculo lambda incorpora dos simplificaciones que simplifican esta semántica. La primera simplificación es que el cálculo lambda trata las funciones de forma "anónima", sin darles nombres explícitos. Por ejemplo, la función

se puede reescribir de forma anónima como

(que se lee como "una tupla de x y y se asignan a "). Del mismo modo, la función

se puede reescribir de forma anónima como

donde la entrada simplemente se asigna a sí misma.

La segunda simplificación es que el cálculo lambda solo usa funciones de una sola entrada. Una función ordinaria que requiere dos entradas, por ejemplo la función, se puede reelaborar en una función equivalente que acepta una sola entrada y, como salida, devuelve otra función, que a su vez acepta una sola entrada. Por ejemplo,

se puede reelaborar en

Este método, conocido como currización , transforma una función que toma múltiples argumentos en una cadena de funciones, cada una con un solo argumento.

La aplicación de la función de la función a los argumentos (5, 2), produce a la vez

,

mientras que la evaluación de la versión al curry requiere un paso más

// se ha utilizado la definición de en la expresión interna. Esto es como una reducción β.
// la definición de se ha utilizado con . Nuevamente, similar a la reducción β.

para llegar al mismo resultado.

El cálculo lambda

El cálculo lambda consiste en un lenguaje de términos lambda , que se definen mediante una determinada sintaxis formal, y un conjunto de reglas de transformación, que permiten la manipulación de los términos lambda. Estas reglas de transformación pueden verse como una teoría de ecuaciones o como una definición operativa .

Como se describió anteriormente, todas las funciones en el cálculo lambda son funciones anónimas, sin nombre. Solo aceptan una variable de entrada, y el curry se utiliza para implementar funciones con varias variables.

Términos lambda

La sintaxis del cálculo lambda define algunas expresiones como expresiones válidas de cálculo lambda y otras como inválidas, al igual que algunas cadenas de caracteres son programas C válidos y otras no. Una expresión de cálculo lambda válida se denomina "término lambda".

Las siguientes tres reglas dan una definición inductiva que se puede aplicar para construir todos los términos lambda sintácticamente válidos:

  • una variable, es en sí misma un término lambda válido
  • si es un término lambda y es una variable, entonces (a veces escrito en ASCII como ) es un término lambda (llamado abstracción );
  • si y son términos lambda, entonces es un término lambda (llamado aplicación ).

Nada más es un término lambda. Por lo tanto, un término lambda es válido si y solo si se puede obtener mediante la aplicación repetida de estas tres reglas. Sin embargo, se pueden omitir algunos paréntesis de acuerdo con ciertas reglas. Por ejemplo, los paréntesis más externos no suelen estar escritos. Consulte la notación a continuación.

Una abstracción es una definición de una función anónima que es capaz de tomar una sola entrada y sustituirla en la expresión . Por tanto, define una función anónima que toma y devuelve . Por ejemplo, es una abstracción para la función que usa el término para . La definición de una función con una abstracción simplemente "configura" la función pero no la invoca. La abstracción une la variable en el término .

Una aplicación representa la aplicación de una función a una entrada , es decir, representa el acto de llamar a una función en la entrada para producir .

No hay ningún concepto en el cálculo lambda de declaración de variable. En una definición como (ie ), el cálculo lambda trata como una variable que aún no está definida. La abstracción es sintácticamente válida y representa una función que agrega su entrada a lo aún desconocido .

Se pueden usar corchetes y pueden ser necesarios para eliminar la ambigüedad de los términos. Por ejemplo, y denotan diferentes términos (aunque casualmente se reducen al mismo valor). Aquí, el primer ejemplo define una función cuyo término lambda es el resultado de aplicar x a la función secundaria, mientras que el segundo ejemplo es la aplicación de la función más externa a la entrada x, que devuelve la función secundaria. Por tanto, ambos ejemplos evalúan la función identidad .

Funciones que operan en funciones

En el cálculo lambda, las funciones se toman como ' valores de primera clase ', por lo que las funciones pueden usarse como entradas o devolverse como salidas de otras funciones.

Por ejemplo, representa la función identidad , y representa la función identidad aplicada a . Además, representa la función constante , la función que siempre regresa , sin importar la entrada. En el cálculo lambda, la aplicación de funciones se considera asociativa por la izquierda , es decir .

Hay varias nociones de "equivalencia" y "reducción" que permiten "reducir" los términos lambda a términos lambda "equivalentes".

Equivalencia alfa

Una forma básica de equivalencia, definible en términos lambda, es la equivalencia alfa. Captura la intuición de que la elección particular de una variable ligada, en una abstracción, no importa (normalmente). Por ejemplo, y son términos lambda equivalentes a alfa, y ambos representan la misma función (la función de identidad). Los términos y no son equivalentes alfa, porque no están ligados a una abstracción. En muchas presentaciones, es habitual identificar términos lambda equivalentes a alfa.

Las siguientes definiciones son necesarias para poder definir la reducción β:

Variables libres

Las variables libres de un término son aquellas variables que no están limitadas por una abstracción. El conjunto de variables libres de una expresión se define inductivamente:

  • Las variables libres de son solo
  • El conjunto de variables libres de es el conjunto de variables libres de , pero con eliminado
  • El conjunto de variables libres de es la unión del conjunto de variables libres de y el conjunto de variables libres de .

Por ejemplo, el término lambda representa la identidad no tiene variables libres, pero la función tiene una única variable libre, .

Sustituciones que evitan la captura

Supongamos , y son términos lambda y e son variables. La notación indica sustitución de para en una captura evitando manera. Esto se define de manera que:

  • ;
  • si ;
  • ;
  • ;
  • si y no está en las variables libres de . Se dice que la variable es "fresca" para .

Por ejemplo , y .

La condición de frescura (que requiere que no esté en las variables libres de ) es crucial para asegurar que la sustitución no cambie el significado de las funciones. Por ejemplo, una sustitución se hace que ignora la condición de frescura: . Esta sustitución convierte la función constante en la identidad por sustitución.

En general, el incumplimiento de la condición de frescura puede remediarse mediante un cambio de nombre alfa con una variable de frescura adecuada. Por ejemplo, volviendo a nuestra noción correcta de sustitución, en la abstracción se puede cambiar el nombre con una nueva variable , para obtener , y el significado de la función se conserva por sustitución.

β-reducción

La regla de reducción β establece que una aplicación de la forma se reduce al término . La notación se usa para indicar que β-se reduce a . Por ejemplo, para cada , . Esto demuestra que realmente es la identidad. De manera similar, lo que demuestra que es una función constante.

El cálculo lambda puede verse como una versión idealizada de un lenguaje de programación funcional, como Haskell o Standard ML . Bajo este punto de vista, la reducción β corresponde a un paso computacional. Este paso puede repetirse mediante reducciones β adicionales hasta que no queden más aplicaciones que reducir. En el cálculo lambda sin tipo, como se presenta aquí, este proceso de reducción puede no terminar. Por ejemplo, considere el término . Aquí . Es decir, el término se reduce a sí mismo en una sola reducción β y, por lo tanto, el proceso de reducción nunca terminará.

Otro aspecto del cálculo lambda sin tipo es que no distingue entre diferentes tipos de datos. Por ejemplo, puede ser deseable escribir una función que solo opere con números. Sin embargo, en el cálculo lambda sin tipo, no hay forma de evitar que una función se aplique a valores de verdad , cadenas u otros objetos que no sean números.

Definicion formal

Definición

Las expresiones lambda se componen de:

  • variables v 1 , v 2 , ...;
  • los símbolos de abstracción λ (lambda) y. (punto);
  • paréntesis ().

El conjunto de expresiones lambda, Λ , se puede definir inductivamente :

  1. Si x es una variable, entonces x ∈ Λ.
  2. Si x es una variable y M ∈ Λ, entonces x . M ) ∈ Λ.
  3. Si M , N ∈ Λ, entonces ( MN ) ∈ Λ.

Las instancias de la regla 2 se conocen como abstracciones y las instancias de la regla 3 se conocen como aplicaciones .

Notación

Para mantener ordenada la notación de las expresiones lambda, generalmente se aplican las siguientes convenciones:

  • Los paréntesis más externos se eliminan: M N en lugar de ( M N ).
  • Se supone que las aplicaciones son asociativas por la izquierda: M N P se puede escribir en lugar de (( M N ) P ).
  • El cuerpo de una abstracción se extiende lo más a la derecha posible : λ x . MN significa λ x . ( MN ) y no (λ x . M ) N .
  • Se contrae una secuencia de abstracciones: λ xyz . N se abrevia como λ xyz . N .

Variables libres y ligadas

Se dice que el operador de abstracción, λ, vincula su variable dondequiera que ocurra en el cuerpo de la abstracción. Se dice que las variables que caen dentro del alcance de una abstracción están vinculadas . En una expresión λ x . M , la parte λ x es a menudo llamado ligante , como un indicio de que la variable x se está obligado añadiendo λ x a M . Todas las demás variables se denominan libres . Por ejemplo, en la expresión λ y . xxy , y es una variable ligada y x es una variable libre. Además, una variable está limitada por su abstracción más cercana. En el siguiente ejemplo, la única aparición de x en la expresión está vinculada por la segunda lambda: λ x . yx . zx ).

El conjunto de variables libres de una expresión lambda, M , se denota como FV ( M ) y se define por recursividad en la estructura de los términos, como sigue:

  1. FV ( x ) = { x }, donde x es una variable.
  2. FV (λ x . M ) = FV ( M ) \ { x }.
  3. FV ( MN ) = FV ( M ) ∪ FV ( N ).

Se dice que una expresión que no contiene variables libres es cerrada . Las expresiones lambda cerradas también se conocen como combinadores y son equivalentes a términos en lógica combinatoria .

Reducción

El significado de las expresiones lambda se define por cómo se pueden reducir las expresiones.

Hay tres tipos de reducción:

  • α-conversión : cambio de variables ligadas;
  • β-reducción : aplicación de funciones a sus argumentos;
  • η-reducción : que captura una noción de extensionalidad.

También hablamos de las equivalencias resultantes: dos expresiones son α-equivalentes , si pueden ser α-convertidas en la misma expresión. La equivalencia β y la equivalencia η se definen de manera similar.

El término redex , abreviatura de expresión reducible , se refiere a subtermos que pueden reducirse mediante una de las reglas de reducción. Por ejemplo, (λ x . M ) N es un β-redex en la expresión de la sustitución de N para x en M . La expresión a la que se reduce un redex se llama su reducto ; la reducción de (λ x . M ) N es M [ x  : = N ].

Si x no es libre en M , λ x . M x es también un η-redex, con un reduct de M .

α-conversión

La conversión α, a veces conocida como cambio de nombre α, permite cambiar los nombres de las variables vinculadas. Por ejemplo, α-conversión de λ x . x podría producir λ y . y . Los términos que difieren solo en la conversión α se denominan equivalentes α . Con frecuencia, en los usos del cálculo lambda, los términos α-equivalentes se consideran equivalentes.

Las reglas precisas para la conversión α no son del todo triviales. Primero, cuando se convierte en α una abstracción, las únicas ocurrencias de variables que se renombran son aquellas que están vinculadas a la misma abstracción. Por ejemplo, una conversión α de λ xx . x podría resultar en λ yx . x , pero podría no resultar en λ yx . y . Este último tiene un significado diferente al original. Esto es análogo a la noción de programación del sombreado de variables .

En segundo lugar, la conversión α no es posible si da como resultado que una variable sea capturada por una abstracción diferente. Por ejemplo, si reemplazamos x con y en λ xy . x , obtenemos λ yy . y , que no es en absoluto lo mismo.

En los lenguajes de programación con alcance estático, la conversión α se puede utilizar para simplificar la resolución de nombres asegurándose de que ningún nombre de variable enmascare un nombre en un alcance contenedor (consulte el cambio de nombre α para hacer que la resolución de nombres sea trivial ).

En la notación del índice de De Bruijn , cualesquiera dos términos α-equivalentes son sintácticamente idénticos.

Sustitución

Cambio en el escrito M [ V  : = N ], es el proceso de sustitución de todos los libres ocurrencias de la variable V en la expresión M con la expresión N . La sustitución en términos del cálculo lambda se define por recursividad en la estructura de términos, como sigue (nota: xey son solo variables, mientras que M y N son cualquier expresión lambda):

x [ x  : = N ] = N
y [ x  : = N ] = y , si xy
( M 1 M 2 ) [ x  : = N ] = ( M 1 [ x  : = N ]) ( M 2 [ x  : = N ])
x . M ) [ x  : = N ] = λ x . METRO
y . M ) [ x  : = N ] = λ y . ( M [ x  : = N ]), si xy e y ∉ FV ( N )

Para sustituirlo en una abstracción, a veces es necesario α-convertir la expresión. Por ejemplo, no es correcto que (λ x . Y ) [ y  : = x ] resulte en λ x . x , porque se suponía que la x sustituida era libre, pero acabó ligada. La sustitución correcta en este caso es λ z . x , hasta α-equivalencia. La sustitución se define de forma única hasta la equivalencia α.

β-reducción

La reducción β captura la idea de la aplicación de funciones. β-reducción se define en términos de sustitución: la β-reducción de (λ V . M ) N es M [ V  : = N ].

Por ejemplo, asumiendo alguna codificación de 2, 7, ×, tenemos la siguiente reducción β: (λ n . N × 2) 7 → 7 × 2.

Se puede considerar que la reducción β es lo mismo que el concepto de reducibilidad local en la deducción natural , a través del isomorfismo de Curry-Howard .

η-reducción

η-reducción expresa la idea de extensionalidad , que en este contexto es que dos funciones son iguales si y solo si dan el mismo resultado para todos los argumentos. La reducción η convierte entre λ x . f x y f siempre que x no aparezca libre en f .

Se puede ver que la η-reducción es lo mismo que el concepto de completitud local en la deducción natural , a través del isomorfismo de Curry-Howard .

Formas normales y confluencia

Para el cálculo lambda no tipificado, la reducción β como regla de reescritura no normaliza fuertemente ni normaliza débilmente .

Sin embargo, se puede demostrar que la reducción β es confluente cuando se trabaja hasta la conversión α (es decir, consideramos que dos formas normales son iguales si es posible convertir α una en la otra).

Por lo tanto, tanto los términos fuertemente normalizados como los términos débilmente normalizados tienen una forma normal única. Para términos fuertemente normalizados, se garantiza que cualquier estrategia de reducción producirá la forma normal, mientras que para términos débilmente normalizados, algunas estrategias de reducción pueden fallar en encontrarla.

Codificación de tipos de datos

El cálculo lambda básico se puede utilizar para modelar valores booleanos, aritméticos , estructuras de datos y recursividad, como se ilustra en las siguientes subsecciones.

Aritmética en cálculo lambda

Hay varias formas posibles de definir los números naturales en el cálculo lambda, pero las más comunes son los números de la Iglesia , que se pueden definir de la siguiente manera:

0: = λ fx . X
1: = λ fx . f x
2: = λ fx . f ( f x )
3: = λ fx . f ( f ( f x ))

etcétera. O usando la sintaxis alternativa presentada anteriormente en Notación :

0: = λ fx . X
1: = λ fx . f x
2: = λ fx . f ( f x )
3: = λ fx . f ( f ( f x ))

Un numeral de Church es una función de orden superior: toma una función f de un solo argumento y devuelve otra función de un solo argumento. El numeral de Church n es una función que toma una función f como argumento y devuelve la n -ésima composición de f , es decir, la función f compuesta consigo misma n veces. Esto se denota f ( n ) y es de hecho la n -ésima potencia de f (considerada como un operador); f (0) se define como la función de identidad. Tales composiciones repetidas (de una sola función f ) obedecen a las leyes de los exponentes , razón por la cual estos números pueden usarse para aritmética. (En el cálculo lambda original de Church, se requería que el parámetro formal de una expresión lambda ocurriera al menos una vez en el cuerpo de la función, lo que hacía imposible la definición anterior de 0 ).

Una forma de pensar sobre el número n de la Iglesia , que a menudo es útil al analizar programas, es como una instrucción 'repetir n veces'. Por ejemplo, utilizando las funciones PAIR y NIL definidas a continuación, se puede definir una función que construya una lista (vinculada) de n elementos todos iguales ax repitiendo 'anteponer otro elemento x ' n veces, comenzando desde una lista vacía. El término lambda es

λ nx . n (PAR x ) NINGUNO

Variando lo que se repite, y variando el argumento al que se aplica esa función que se repite, se pueden lograr muchos efectos diferentes.

Podemos definir una función sucesora, que toma un número de Church n y devuelve n + 1 agregando otra aplicación de f , donde '(mf) x' significa que la función 'f' se aplica 'm' veces en 'x':

SUCC: = λ nfx . f ( n f x )

Debido a que la m -ésima composición de f compuesta con la n -ésima composición de f da la m + n -ésima composición de f , la adición se puede definir de la siguiente manera:

MÁS: = λ mnfx . m f ( n f x )

PLUS se puede considerar como una función que toma dos números naturales como argumentos y devuelve un número natural; se puede verificar que

MÁS 2 3

y

5

son expresiones lambda equivalentes a β. Dado que sumar m a un número n se puede lograr sumando 1 m veces, una definición alternativa es:

MÁS: = λ mn . m SUCC n 

De manera similar, la multiplicación se puede definir como

MULT: = λ mnf . m ( n f )

Alternativamente

MULT: = λ mn . m (MÁS n ) 0

desde multiplicando m y n es la misma que la repetición de los add n función de m veces y luego aplicarlo a cero. La exponenciación tiene una interpretación bastante simple en los números de la Iglesia, a saber

POW: = λ be . e b

La función predecesora definida por PRED n = n - 1 para un entero positivo n y PRED 0 = 0 es considerablemente más difícil. La formula

PRED: = λ nfx . ngh . h ( g f )) (λ u . x ) (λ u . u )

se puede validar mostrando inductivamente que si T denota gh . h ( g f )) , entonces T ( n )u . x ) = (λ h . h ( f ( n −1) ( x ))) para n > 0 . A continuación se dan otras dos definiciones de PRED , una que usa condicionales y la otra que usa pares . Con la función predecesora, la resta es sencilla. Definiendo

SUB: = λ mn . n PRED m ,

SUB m n produce m - n cuando m > n y 0 en caso contrario.

Lógica y predicados

Por convención, las siguientes dos definiciones (conocidas como booleanos de la Iglesia) se utilizan para los valores booleanos VERDADERO y FALSO :

VERDADERO: = λ xy . X
FALSO: = λ xy . y
(Tenga en cuenta que FALSO es equivalente al número cero de la Iglesia definido anteriormente)

Luego, con estos dos términos lambda, podemos definir algunos operadores lógicos (estas son solo posibles formulaciones; otras expresiones son igualmente correctas):

Y: = λ pq . p q p
O: = λ pq . p p q
NO: = λ p . p FALSO VERDADERO
SI ENTONCES: = λ pab . p a b

Ahora podemos calcular algunas funciones lógicas, por ejemplo:

Y VERDAD FALSO
≡ (λ pq . P q p ) VERDADERO FALSO → β VERDADERO FALSO VERDADERO
≡ (λ xy . X ) FALSO VERDADERO → β FALSO

y vemos que Y VERDADERO FALSO es equivalente a FALSO .

Un predicado es una función que devuelve un valor booleano. El predicado más fundamental es ISZERO , que devuelve VERDADERO si su argumento es el número 0 de la Iglesia y FALSO si su argumento es cualquier otro número de la Iglesia:

ISZERO: = λ n . nx .FALSE) VERDADERO

El siguiente predicado prueba si el primer argumento es menor o igual que el segundo:

LEQ: = λ mn .ISZERO (SUB m n ) ,

y dado que m = n , si LEQ m n y LEQ n m , es sencillo construir un predicado para la igualdad numérica.

La disponibilidad de predicados y la definición anterior de TRUE y FALSE hacen que sea conveniente escribir expresiones "if-then-else" en el cálculo lambda. Por ejemplo, la función predecesora se puede definir como:

PRED: = λ n . ngk .ISZERO ( g 1) k (PLUS ( g k ) 1)) (λ v .0) 0

lo cual se puede verificar mostrando inductivamente que ngk .ISZERO ( g 1) k (PLUS ( g k ) 1)) (λ v .0) es la función suma n - 1 para n > 0.

Pares

Un par (2-tupla) se puede definir en términos de VERDADERO y FALSO , utilizando la codificación Church para pares . Por ejemplo, PAIR encapsula el par ( x , y ), FIRST devuelve el primer elemento del par y SEGUNDO devuelve el segundo.

PAR: = λ xyf . f x y
PRIMERO: = λ p . p VERDADERO
SEGUNDO: = λ p . p FALSO
NULO: = λ x .TRUE
NULO: = λ p . pxy .FALSE)

Una lista enlazada se puede definir como NIL para la lista vacía o como PAR de un elemento y una lista más pequeña. El predicado NULL prueba el valor NIL . (Alternativamente, con NIL: = FALSE , la construcción lhtz .deal_with_head_ h _and_tail_ t ) (deal_with_nil) obvia la necesidad de una prueba NULL explícita).

Como ejemplo del uso de pares, la función de desplazamiento e incremento que asigna ( m , n ) a ( n , n + 1) se puede definir como

Φ: = λ x .PAR (SEGUNDO x ) (SUCC (SEGUNDO x ))

lo que nos permite dar quizás la versión más transparente de la función predecesora:

PRED: = λ n. PRIMERO ( n Φ (PAR 0 0)).

Técnicas de programación adicionales

Existe una cantidad considerable de modismos de programación para el cálculo lambda. Muchos de estos se desarrollaron originalmente en el contexto del uso del cálculo lambda como base para la semántica del lenguaje de programación , utilizando efectivamente el cálculo lambda como un lenguaje de programación de bajo nivel . Debido a que varios lenguajes de programación incluyen el cálculo lambda (o algo muy similar) como un fragmento, estas técnicas también se usan en la programación práctica, pero luego pueden percibirse como oscuras o extrañas.

Constantes nombradas

En el cálculo lambda, una biblioteca tomaría la forma de una colección de funciones previamente definidas, que como términos lambda son simplemente constantes particulares. El cálculo lambda puro no tiene un concepto de constantes nombradas ya que todos los términos lambda atómicos son variables, pero se puede emular tener constantes nombradas dejando a un lado una variable como el nombre de la constante, usando la abstracción para vincular esa variable en el cuerpo principal y aplique esa abstracción a la definición deseada. Por lo tanto, para usar f para significar M (algún término lambda explícito) en N (otro término lambda, el "programa principal"), se puede decir

f . N ) M

Los autores a menudo introducen azúcar sintáctico , como let , para permitir escribir lo anterior en el orden más intuitivo.

sea f = M en N

Al encadenar tales definiciones, se puede escribir un "programa" de cálculo lambda como cero o más definiciones de función, seguido de un término lambda utilizando esas funciones que constituyen el cuerpo principal del programa.

Una restricción notable de este let es que el nombre f no está definido en M , ya que M está fuera del alcance de la abstracción vinculante f ; esto significa que una definición de función recursiva no se puede usar como M con let . La construcción sintáctica de azúcar más avanzada de letrec que permite escribir definiciones de funciones recursivas en ese estilo ingenuo, emplea además combinadores de punto fijo.

Recurrencia y puntos fijos

La recursividad es la definición de una función que utiliza la propia función. El cálculo lambda no puede expresar esto tan directamente como algunas otras notaciones: todas las funciones son anónimas en el cálculo lambda, por lo que no podemos referirnos a un valor que aún no se ha definido, dentro del término lambda que define ese mismo valor. Sin embargo, la recursión todavía se puede conseguir mediante la disposición de una expresión lambda para recibir como su valor de argumento, por ejemplo en  x . X x ) E .

Considere la función factorial F ( n ) definida recursivamente por

F ( n ) = 1, si n = 0; de lo contrario n × F ( n - 1) .

En la expresión lambda que va a representar esta función, se supondrá que un parámetro (normalmente el primero) recibe la expresión lambda en sí como su valor, de modo que llamarlo, aplicarlo a un argumento, equivaldrá a una recursividad. Por lo tanto, para lograr la recursividad, el argumento pretendido como autorreferencial (llamado r aquí) siempre debe pasarse a sí mismo dentro del cuerpo de la función, en un punto de llamada:

G: = λ r . λ n . (1, si n = 0; si no n × ( r r ( n −1)))
con  r r x = F x = G r x  para mantener, entonces  {{{1}}}  y
F: = GG = (λ x . X x ) G

La autoaplicación logra la replicación aquí, pasando la expresión lambda de la función a la siguiente invocación como un valor de argumento, haciéndola disponible para ser referenciada y llamada allí.

Esto lo resuelve, pero requiere volver a escribir cada llamada recursiva como autoaplicación. Nos gustaría tener una solución genérica, sin necesidad de reescrituras:

G: = λ r . λ n . (1, si n = 0; si no n × ( r ( n −1)))
con  r x = F x = G r x  para mantener, entonces  r = G r =: FIX G  y
F: = FIX G  donde  FIX g  : = ( r donde r = g r ) = g (FIX g )
de modo que  FIX G = G (FIX G) = (λ n . (1, if n = 0; else n × ((FIX G) ( n −1))))

Dado un término lambda con el primer argumento que representa una llamada recursiva (por ejemplo, G aquí), el combinador de punto fijo FIX devolverá una expresión lambda autorreplicante que representa la función recursiva (aquí, F ). La función no necesita pasarse explícitamente a sí misma en ningún momento, ya que la autorreplicación se organiza de antemano, cuando se crea, para que se realice cada vez que se llame. Así, la expresión lambda original (FIX G) se recrea dentro de sí misma, en el punto de llamada, logrando la autorreferencia .

De hecho, hay muchas definiciones posibles para este operador FIX , siendo la más simple:

Y  : = λ g . (Λ x . G ( x x )) (λ x . G ( x x ))

En el cálculo lambda, Y g   es un punto fijo de g , ya que se expande a:

Y g
alturax . altura ( x x )) (λ x . altura ( x x ))) g
x . g ( x x )) (λ x . g ( x x ))
g ((λ x . g ( x x )) (λ x . g ( x x )))
g ( Y g )

Ahora, para realizar nuestra llamada recursiva a la función factorial, simplemente llamaríamos ( Y G) n , donde n es el número del que estamos calculando el factorial. Dado n = 4, por ejemplo, esto da:

( Y G) 4
G ( Y G) 4
rn . (1, si n = 0; de lo contrario n × ( r ( n −1)))) ( Y G) 4
n . (1, si n = 0; de lo contrario n × (( Y G) ( n −1)))) 4
1, si 4 = 0; más 4 × (( Y G) (4−1))
4 × (G ( Y G) (4−1))
4 × ((λ n . (1, si n = 0; de lo contrario n × (( Y G) ( n −1)))) (4−1))
4 × (1, si 3 = 0; si no 3 × (( Y G) (3−1)))
4 × (3 × (G ( Y G) (3−1)))
4 × (3 × ((λ n . (1, si n = 0; si no, n × (( Y G) ( n −1)))) (3−1)))
4 × (3 × (1, si 2 = 0; si no 2 × (( Y G) (2−1))))
4 × (3 × (2 × (G ( Y G) (2−1))))
4 × (3 × (2 × ((λ n . (1, si n = 0; si no n × (( Y G) ( n −1)))) (2−1))))
4 × (3 × (2 × (1, si 1 = 0; si no 1 × (( Y G) (1−1)))))
4 × (3 × (2 × (1 × (G ( Y G) (1−1)))))
4 × (3 × (2 × (1 × ((λ n . (1, si n = 0; de lo contrario n × (( Y G) ( n −1)))) (1−1)))))
4 × (3 × (2 × (1 × (1, si 0 = 0; de lo contrario 0 × (( Y G) (0−1))))))
4 × (3 × (2 × (1 × (1))))
24

Cada función definida de forma recursiva puede verse como un punto fijo de alguna función adecuadamente definida que se cierra sobre la llamada recursiva con un argumento adicional y, por lo tanto, utilizando Y , cada función definida de forma recursiva se puede expresar como una expresión lambda. En particular, ahora podemos definir claramente el predicado de resta, multiplicación y comparación de números naturales de forma recursiva.

Cláusulas estándar

Ciertos términos tienen nombres comúnmente aceptados:

Yo  : = λ x . X
K  : = λ xy . X
S  : = λ xyz . x z ( y z )
B  : = λ xyz . x ( y z )
C  : = λ xyz . x z y
W  : = λ xy . x y y
U  : = λ x . x x
ω  : = λ x . x x
Ω  : = ω ω
Y  : = λ g . (Λ x . G ( x x )) (λ x . G ( x x ))

Varios de estos tienen aplicaciones directas en la eliminación de la abstracción que convierte los términos lambda en términos de cálculo combinador .

Eliminación de abstracción

Si N es un término lambda sin abstracción, pero que posiblemente contenga constantes nombradas ( combinadores ), entonces existe un término lambda T ( x , N ) que es equivalente a λ x . N pero carece de abstracción (excepto como parte de las constantes nombradas, si estas se consideran no atómicas). Esto también se puede ver como variables anonimizadas, ya que T ( x , N ) elimina todas las apariciones de x de N , al tiempo que permite que los valores de los argumentos se sustituyan en las posiciones donde N contiene una x . La función de conversión T se puede definir mediante:

T ( x , x ): = yo
T ( x , N ): = K N si x no es libre en N .
T ( x , M N ): = S T ( x , M ) T ( x , N )

En cualquier caso, un término de la forma T ( x , N ) P se puede reducir haciendo que el combinador inicial I , K o S tome el argumento P , tal como lo haría la reducción β de x . N ) P. Me devuelve ese argumento. K lanza el argumento de distancia, al igual que x . N ) haría si x no tiene ninguna ocurrencia libre en N . S pasa el argumento a ambos subterráneos de la aplicación y luego aplica el resultado del primero al resultado del segundo.

Los combinadores B y C son similares a S , pero pasan el argumento a un solo subtermo de una aplicación ( B al subtermo "argumento" y C al subtermo "función"), ahorrando así un K subsiguiente si no hay ocurrencia de x en un subterráneo. En comparación con B y C , el combinador S en realidad combina dos funcionalidades: reorganizar argumentos y duplicar un argumento para que pueda usarse en dos lugares. El combinador W solo hace lo último, produciendo el sistema B, C, K, W como una alternativa al cálculo del combinador SKI .

Cálculo lambda mecanografiado

Un cálculo lambda con tipo es un formalismo con tipo que utiliza el símbolo lambda ( ) para denotar la abstracción de funciones anónimas. En este contexto, los tipos suelen ser objetos de naturaleza sintáctica que se asignan a términos lambda; la naturaleza exacta de un tipo depende del cálculo considerado (consulte Tipos de cálculos lambda tipificados ). Desde cierto punto de vista, los cálculos lambda tipificados pueden verse como refinamientos del cálculo lambda no tipificado, pero desde otro punto de vista, también pueden considerarse la teoría más fundamental y el cálculo lambda no tipificado es un caso especial con un solo tipo.

Los cálculos lambda tipificados son lenguajes de programación fundamentales y son la base de los lenguajes de programación funcionales tipificados como ML y Haskell y, más indirectamente, los lenguajes de programación imperativos tipificados . Los cálculos lambda tipificados juegan un papel importante en el diseño de sistemas de tipos para lenguajes de programación; en este caso, la tipificación suele capturar propiedades deseables del programa, por ejemplo, el programa no provocará una infracción de acceso a la memoria.

Los cálculos lambda tipificados están estrechamente relacionados con la lógica matemática y la teoría de la prueba a través del isomorfismo de Curry-Howard y pueden considerarse como el lenguaje interno de clases de categorías , por ejemplo, el cálculo lambda simplemente tipificado es el lenguaje de categorías cerradas cartesianas (CCC).

Estrategias de reducción

Si un término se normaliza o no, y cuánto trabajo hay que hacer para normalizarlo, depende en gran medida de la estrategia de reducción utilizada. Las estrategias comunes de reducción del cálculo lambda incluyen:

Orden normal
La redex más a la izquierda y más externa siempre se reduce primero. Es decir, siempre que sea posible, los argumentos se sustituyen en el cuerpo de una abstracción antes de que se reduzcan.
Orden de aplicación
El redex más a la izquierda y más interno siempre se reduce primero. Intuitivamente, esto significa que los argumentos de una función siempre se reducen antes que la función en sí. El orden aplicativo siempre intenta aplicar funciones a formas normales, incluso cuando esto no es posible.
Reducciones β completas
Cualquier redex puede reducirse en cualquier momento. Esto significa esencialmente la falta de una estrategia de reducción en particular; con respecto a la reducibilidad, "todas las apuestas están canceladas".

Las estrategias de reducción débil no reducen bajo abstracciones lambda:

Llamar por valor
Un redex se reduce solo cuando su lado derecho se ha reducido a un valor (variable o abstracción). Solo se reducen los redexes más externos.
Llamar por nombre
Como orden normal, pero no se realizan reducciones dentro de las abstracciones. Por ejemplo, λ x . (Λ x . X ) x está en forma normal según esta estrategia, aunque contiene el redex x . X ) x .

Las estrategias con compartir reducen los cálculos que son "iguales" en paralelo:

Reducción óptima
Como orden normal, pero los cálculos que tienen la misma etiqueta se reducen simultáneamente.
Llame por necesidad
Como llamada por nombre (por lo tanto débil), pero las aplicaciones de función que duplicarían términos en su lugar nombran el argumento, que luego se reduce solo "cuando es necesario".

Computabilidad

No existe un algoritmo que tome como entrada dos expresiones lambda y dé como resultado VERDADERO o FALSO dependiendo de si una expresión se reduce a la otra. Más precisamente, ninguna función computable puede decidir la cuestión. Este fue históricamente el primer problema para el que se pudo probar la indecidibilidad. Como es habitual para tal demostración, computable significa computable por cualquier modelo de computación que sea Turing completo . De hecho, la computabilidad se puede definir a través del cálculo lambda: una función F : NN de números naturales es una función computable si y solo si existe una expresión lambda f tal que para cada par de x , y en N , F ( x ) = y si y sólo si f x  = β  y , donde x y y son los números Iglesia correspondiente a x y y , respectivamente y = β significa equivalencia con β-reducción. Consulte la tesis de Church-Turing para conocer otros enfoques para definir la computabilidad y su equivalencia.

La prueba de incomputabilidad de Church primero reduce el problema a determinar si una expresión lambda dada tiene una forma normal . Luego asume que este predicado es computable y, por lo tanto, puede expresarse en cálculo lambda. Basándose en trabajos anteriores de Kleene y construyendo una numeración de Gödel para expresiones lambda, construye una expresión lambda e que sigue de cerca la demostración del primer teorema de incompletitud de Gödel . Si e se aplica a su propio número de Gödel, se produce una contradicción.

Complejidad

La noción de complejidad computacional para el cálculo lambda es un poco complicada, porque el costo de una reducción β puede variar dependiendo de cómo se implemente. Para ser precisos, de alguna manera se debe encontrar la ubicación de todas las ocurrencias de la variable ligada V en la expresión E , lo que implica un costo de tiempo, o se debe realizar un seguimiento de las ubicaciones de las variables libres de alguna manera, lo que implica un costo de espacio. Una búsqueda naïve para las ubicaciones de V en E es O ( n ) de la longitud n de E . Las cadenas de director fueron un enfoque temprano que intercambió este costo de tiempo por un uso de espacio cuadrático. De manera más general, esto ha llevado al estudio de sistemas que utilizan la sustitución explícita .

En 2014 se demostró que el número de pasos de reducción β tomados por la reducción de orden normal para reducir un plazo es un modelo de costo de tiempo razonable , es decir, la reducción se puede simular en una máquina de Turing en tiempo polinomialmente proporcional al número de pasos . Este era un problema abierto de larga data, debido a la explosión de tamaño , la existencia de términos lambda que crecen exponencialmente en tamaño para cada reducción β. El resultado soluciona esto trabajando con una representación compartida compacta. El resultado deja en claro que la cantidad de espacio necesaria para evaluar un término lambda no es proporcional al tamaño del término durante la reducción. Actualmente no se sabe cuál sería una buena medida de la complejidad espacial.

Un modelo irrazonable no significa necesariamente ineficiente. La reducción óptima reduce todos los cálculos con la misma etiqueta en un paso, evitando el trabajo duplicado, pero el número de pasos de reducción β paralelos para reducir un término dado a la forma normal es aproximadamente lineal en el tamaño del término. Esto es demasiado pequeño para ser una medida de costo razonable, ya que cualquier máquina de Turing puede estar codificada en el cálculo lambda en tamaño linealmente proporcional al tamaño de la máquina de Turing. El verdadero costo de reducir los términos lambda no se debe a la reducción β per se, sino al manejo de la duplicación de redexes durante la reducción β. No se sabe si las implementaciones de reducción óptima son razonables cuando se miden con respecto a un modelo de costo razonable, como el número de pasos más a la izquierda hacia la forma normal, pero se ha demostrado para fragmentos del cálculo lambda que el algoritmo de reducción óptima es eficiente. y tiene como máximo una sobrecarga cuadrática en comparación con el extremo izquierdo-exterior. Además, la implementación del prototipo BOHM de reducción óptima superó tanto a Caml Light como a Haskell en términos puramente lambda.

Cálculo lambda y lenguajes de programación

Como se señaló en el artículo de 1965 de Peter Landin "A Correspondence between ALGOL 60 and Church's Lambda-notation", los lenguajes de programación procedimental secuenciales pueden entenderse en términos del cálculo lambda, que proporciona los mecanismos básicos para la abstracción y el procedimiento procedimentales (subprograma) solicitud.

Funciones anónimas

Por ejemplo, en Lisp, la función "cuadrado" se puede expresar como una expresión lambda de la siguiente manera:

(lambda (x) (* x x))

El ejemplo anterior es una expresión que se evalúa como una función de primera clase. El símbolo lambdacrea una función anónima, dada una lista de nombres de parámetros, (x)- solo un argumento en este caso, y una expresión que se evalúa como el cuerpo de la función (* x x),. Las funciones anónimas a veces se denominan expresiones lambda.

Por ejemplo, Pascal y muchos otros lenguajes imperativos han soportado durante mucho tiempo el paso de subprogramas como argumentos a otros subprogramas a través del mecanismo de punteros de función . Sin embargo, los punteros de función no son una condición suficiente para que las funciones sean tipos de datos de primera clase , porque una función es un tipo de datos de primera clase si y solo si se pueden crear nuevas instancias de la función en tiempo de ejecución. Y esta creación de funciones en tiempo de ejecución está soportada en Smalltalk , JavaScript y Wolfram Language , y más recientemente en Scala , Eiffel ("agentes"), C # ("delegados") y C ++ 11 , entre otros.

Paralelismo y concurrencia

La propiedad de Church-Rosser del cálculo lambda significa que la evaluación (reducción β) se puede realizar en cualquier orden , incluso en paralelo. Esto significa que varias estrategias de evaluación no deterministas son relevantes. Sin embargo, el cálculo lambda no ofrece construcciones explícitas para el paralelismo . Se pueden agregar constructos como Futuros al cálculo lambda. Se han desarrollado otros cálculos de procesos para describir la comunicación y la concurrencia.

Semántica

El hecho de que los términos de cálculo lambda actúen como funciones en otros términos de cálculo lambda, e incluso en sí mismos, generó dudas sobre la semántica del cálculo lambda. ¿Podría asignarse un significado sensible a los términos de cálculo lambda? La semántica natural consistía en encontrar un conjunto D isomorfo al espacio funcional DD , de funciones sobre sí mismo. Sin embargo, no puede existir tal D no trivial , por restricciones de cardinalidad porque el conjunto de todas las funciones de D a D tiene una cardinalidad mayor que D , a menos que D sea ​​un conjunto singleton .

En la década de 1970, Dana Scott demostró que si solo se consideraran funciones continuas, se podría encontrar un conjunto o dominio D con la propiedad requerida, proporcionando así un modelo para el cálculo lambda.

Este trabajo también formó la base para la semántica denotacional de los lenguajes de programación.

Variaciones y ampliaciones

Estas extensiones están en el cubo lambda :

Estos sistemas formales son extensiones del cálculo lambda que no están en el cubo lambda:

Estos sistemas formales son variaciones del cálculo lambda:

Estos sistemas formales están relacionados con el cálculo lambda:

Ver también

Notas

Referencias

Otras lecturas

Monografías / libros de texto para estudiantes de posgrado:

  • Morten Heine Sørensen, Paweł Urzyczyn, Lectures on the Curry-Howard isomorphism , Elsevier, 2006, ISBN  0-444-52077-5 es una monografía reciente que cubre los principales temas del cálculo lambda desde la variedad libre de tipos hasta la mayoría de lambda tipificada. cálculos , incluidos desarrollos más recientes como los sistemas de tipos puros y el cubo lambda . No cubre las extensiones de subtipo .
  • Pierce, Benjamin (2002), Tipos y lenguajes de programación , MIT Press, ISBN 0-262-16209-1cubre los cálculos lambda desde una perspectiva práctica del sistema de tipos; Algunos temas, como los tipos dependientes, solo se mencionan, pero el subtipo es un tema importante.

Algunas partes de este artículo se basan en material de FOLDOC , usado con permiso .

enlaces externos

  1. ^ Iglesia, Alonzo (1941). Los cálculos de conversión lambda . Princeton: Prensa de la Universidad de Princeton . Consultado el 14 de abril de 2020 .
  2. ^ Frink Jr., Orrin (1944). "Revisión: los cálculos de conversión de lambda por la iglesia de Alonzo" (PDF) . Toro. Amer. Matemáticas. Soc . 50 (3): 169-172. doi : 10.1090 / s0002-9904-1944-08090-7 .