El cálculo combinatorio SKI es un sistema de lógica combinatoria y un sistema computacional . Puede considerarse un lenguaje de programación informática, aunque no es conveniente para escribir software. En cambio, es importante en la teoría matemática de algoritmos porque es un lenguaje completo de Turing extremadamente simple . Puede compararse con una versión reducida del cálculo lambda sin tipo . Fue introducido por Moses Schönfinkel [1] y Haskell Curry [2] .
Todas las operaciones del cálculo lambda se pueden codificar mediante eliminación de abstracción en el cálculo SKI como árboles binarios cuyas hojas son uno de los tres símbolos S , K e I (llamados combinadores ).
Aunque la representación más formal de los objetos en este sistema requiere árboles binarios, para una composición tipográfica más sencilla, a menudo se representan como expresiones entre paréntesis, como una abreviatura del árbol que representan. Cualquier subárbol puede estar entre paréntesis, pero a menudo solo se incluyen entre paréntesis los subárboles del lado derecho, con la asociatividad izquierda implícita para cualquier aplicación sin paréntesis. Por ejemplo, ISK significa (( IS ) K ). Usando esta notación, un árbol cuyo subárbol izquierdo es el árbol KS y cuyo subárbol derecho es el árbol SK se puede escribir como KS ( SK ). Si se desea más explicitud, también se pueden incluir los paréntesis implícitos: (( KS )( SK )).
De manera informal, y utilizando la jerga de los lenguajes de programación, un árbol ( xy ) puede considerarse como una función x aplicada a un argumento y . Cuando se evalúa ( es decir , cuando la función se "aplica" al argumento), el árbol "devuelve un valor", es decir , se transforma en otro árbol. La "función", el "argumento" y el "valor" son combinadores o árboles binarios. Si son árboles binarios, también pueden considerarse funciones, si es necesario.
La operación de evaluación se define de la siguiente manera:
( x , y y z representan expresiones formadas a partir de las funciones S , K e I , y establecen valores):
Devuelvo su argumento:
K , cuando se aplica a cualquier argumento x , produce una función constante de un argumento K x , que, cuando se aplica a cualquier argumento y , devuelve x :
S es un operador de sustitución. Toma tres argumentos y luego devuelve el primer argumento aplicado al tercero, que luego se aplica al resultado del segundo argumento aplicado al tercero. Más claramente:
Ejemplo de cálculo: SKSK se evalúa como KK ( SK ) según la regla S. Luego, si evaluamos KK ( SK ), obtenemos K según la regla K. Como no se puede aplicar ninguna otra regla, el cálculo se detiene aquí.
Para todos los árboles x y todos los árboles y , SK xy siempre se evaluará como y en dos pasos, K y ( xy ) = y , por lo que el resultado final de evaluar SK xy siempre será igual al resultado de evaluar y . Decimos que SK x e I son "funcionalmente equivalentes" para cualquier x porque siempre arrojan el mismo resultado cuando se aplican a cualquier y .
A partir de estas definiciones se puede demostrar que el cálculo SKI no es el sistema mínimo que puede realizar completamente los cálculos del cálculo lambda, ya que todas las apariciones de I en cualquier expresión se pueden reemplazar por ( SKK ) o ( SKS ) o ( SK x ) para cualquier x , y la expresión resultante arrojará el mismo resultado. Por lo tanto, la " I " es simplemente azúcar sintáctico . Dado que I es opcional, el sistema también se conoce como cálculo SK o cálculo combinatorio SK .
Es posible definir un sistema completo utilizando un único combinador (impropio). Un ejemplo es el combinador iota de Chris Barker , que se puede expresar en términos de S y K de la siguiente manera:
Es posible reconstruir S , K e I a partir del combinador iota. Al aplicar ι a sí mismo se obtiene ιι = ι SK = SSKK = SK ( KK ) que es funcionalmente equivalente a I . K se puede construir al aplicar ι dos veces a I (lo que es equivalente a la aplicación de ι a sí mismo): ι(ι(ιι)) = ι(ιι SK ) = ι( ISK ) = ι( SK ) = SKSK = K . Al aplicar ι una vez más se obtiene ι(ι(ι(ιι))) = ι K = KSK = S .
El término más simple posible que forma una base es X = λf.f λxyz.xz (yz) λxyz.x, que satisface XX = K y X (XX) = S.
Los términos y derivaciones de este sistema también pueden definirse de manera más formal:
Términos : El conjunto T de términos se define recursivamente mediante las siguientes reglas.
Derivaciones : Una derivación es una secuencia finita de términos definidos recursivamente por las siguientes reglas (donde α e ι son palabras del alfabeto { S , K , I , (, )} mientras que β, γ y δ son términos):
Suponiendo que una secuencia es una derivación válida desde el principio, se puede extender utilizando estas reglas. Todas las derivaciones de longitud 1 son derivaciones válidas.
SII es una expresión que toma un argumento y aplica ese argumento a sí mismo:
También se lo conoce como combinador U , U x = xx . Una propiedad interesante de este es que su autoaplicación es irreducible:
O, utilizando la ecuación como su definición directamente, obtenemos inmediatamente U U = U U .
Otra cosa es que permite escribir una función que aplica una cosa a la autoaplicación de otra cosa:
o puede verse como la definición directa de otro combinador, H xy = x ( yy ).
Esta función se puede utilizar para lograr la recursión . Si β es la función que aplica α a la autoaplicación de algo más,
entonces la autoaplicación de este β es el punto fijo de ese α:
O, directamente de nuevo de la definición derivada, H α( H α) = α( H α( H α)).
Si α expresa un "paso computacional" calculado por αρν para algún ρ y ν, que supone que ρν' expresa "el resto del cálculo" (para algún ν' que α "calculará" a partir de ν), entonces su punto fijo ββ expresa todo el cálculo recursivo, ya que usar la misma función ββ para la llamada al "resto del cálculo" (con ββν = α(ββ)ν) es la definición misma de recursión: ρν' = ββν' = α(ββ)ν' = ... . α tendrá que emplear algún tipo de condicional para detenerse en algún "caso base" y no hacer la llamada recursiva entonces, para evitar la divergencia.
Esto se puede formalizar con
como
lo que nos da una posible codificación del combinador Y. Una variación más corta reemplaza sus dos subtérminos principales solo con SSI , ya que H α( H α) = SHH α = SSIH α.
Esto se vuelve mucho más corto con el uso de los combinadores B, C, W , como equivalente
Y con una sintaxis pseudo- Haskell se convierte en el excepcionalmente corto Y = U . (. U ).
Siguiendo este enfoque, son posibles otras definiciones de combinadores de puntos fijos, como
o cualquier otra definición del combinador H intermediario (donde vale cualquier cosa en lugar de "_"), con su definición Y correspondiente para ponerlo en marcha correctamente.
S ( K ( SI )) K invierte los dos términos que le siguen:
Por lo tanto, es equivalente a CI . Y en general, S ( K ( S f )) K es equivalente a C f , para cualquier f .
El cálculo combinador SKI también puede implementar la lógica booleana en forma de una estructura if-then-else . Una estructura if-then-else consiste en una expresión booleana que es verdadera ( T ) o falsa ( F ) y dos argumentos, tales que:
y
La clave está en definir las dos expresiones booleanas. La primera funciona como uno de nuestros combinadores básicos:
El segundo también es bastante simple:
Una vez definidos verdadero y falso, toda la lógica booleana se puede implementar en términos de estructuras if-then-else .
El NOT booleano (que devuelve el opuesto de un booleano dado) funciona de la misma manera que la estructura if-then-else , con F y T como segundo y tercer valor, por lo que se puede implementar como una operación de sufijo:
Si esto se coloca en una estructura if-then-else , se puede demostrar que tiene el resultado esperado.
El OR booleano (que devuelve T si cualquiera de los dos valores booleanos que lo rodean es T ) funciona de la misma manera que una estructura if-then-else con T como segundo valor, por lo que se puede implementar como una operación infija:
Si esto se coloca en una estructura if-then-else , se puede demostrar que tiene el resultado esperado:
El operador booleano AND (que devuelve T si ambos valores booleanos que lo rodean son T ) funciona de la misma manera que una estructura if-then-else con F como tercer valor, por lo que se puede implementar como una operación de sufijo:
Si esto se coloca en una estructura if-then-else , se puede demostrar que tiene el resultado esperado:
Dado que esto define T , F , NOT (como un operador postfijo), OR (como un operador infijo) y AND (como un operador postfijo) en términos de notación SKI, esto demuestra que el sistema SKI puede expresar completamente la lógica booleana.
Como el cálculo SKI está completo , también es posible expresar NOT , OR y AND como operadores de prefijo:
Los combinadores K y S corresponden a dos axiomas bien conocidos de la lógica sentencial :
La aplicación de la función corresponde a la regla modus ponens :
Los axiomas AK y AS y la regla MP son completos para el fragmento implicacional de la lógica intuicionista . Para que la lógica combinatoria tenga como modelo:
Esta conexión entre los tipos de combinadores y los axiomas lógicos correspondientes es un ejemplo del isomorfismo de Curry-Howard .
Puede haber varias formas de hacer una reducción. Todas son equivalentes, siempre que no se altere el orden de las operaciones.