Recursión anónima
Un combinador de punto fijo es una función de orden superior fix
que, por definición, satisface la equivalencia.
forall f. fix f = f (fix f)
fix f
representa una solución x
a la ecuación de punto fijo
x = f x
El factorial de un número natural puede ser probado por
fact 0 = 1
fact n = n * fact (n - 1)
Utilizando fix
, las pruebas constructivas arbitrarias sobre funciones generales / μ-recursivas se pueden derivar sin autorreferencialidad anónima.
fact n = (fix fact') n
dónde
fact' rec n = if n == 0
then 1
else n * rec (n - 1)
tal que
fact 3
= (fix fact') 3
= fact' (fix fact') 3
= if 3 == 0 then 1 else 3 * (fix fact') (3 - 1)
= 3 * (fix fact') 2
= 3 * fact' (fix fact') 2
= 3 * if 2 == 0 then 1 else 2 * (fix fact') (2 - 1)
= 3 * 2 * (fix fact') 1
= 3 * 2 * fact' (fix fact') 1
= 3 * 2 * if 1 == 0 then 1 else 1 * (fix fact') (1 - 1)
= 3 * 2 * 1 * (fix fact') 0
= 3 * 2 * 1 * fact' (fix fact') 0
= 3 * 2 * 1 * if 0 == 0 then 1 else 0 * (fix fact') (0 - 1)
= 3 * 2 * 1 * 1
= 6
Esta prueba formal de que
fact 3 = 6
utiliza metódicamente la equivalencia del combinador de punto fijo para reescrituras
fix fact' -> fact' (fix fact')
Cálculo lambda
El formalismo de cálculo lambda sin tipo consiste en una gramática libre de contexto
E ::= v Variable
| λ v. E Abstraction
| E E Application
donde v
rangos sobre variables, junto con las reglas de reducción beta y eta
(λ x. B) E -> B[x := E] Beta
λ x. E x -> E if x doesn’t occur free in E Eta
La reducción beta sustituye todas las ocurrencias libres de la variable x
en el cuerpo de abstracción ("función") B
por la expresión ("argumento") E
. La reducción de Eta elimina la abstracción redundante. A veces se omite del formalismo. Una expresión irreducible , a la que no se aplica ninguna regla de reducción, está en forma normal o canónica .
λ x y. E
es taquigrafía para
λ x. λ y. E
(abstracción multiarity),
E F G
es taquigrafía para
(E F) G
(aplicación izquierda-asociatividad),
λ x. x
y
λ y. y
son alfa-equivalente .
La abstracción y la aplicación son las dos únicas "primitivas del lenguaje" del cálculo lambda, pero permiten la codificación de datos y operaciones arbitrariamente complejas.
Los números de la Iglesia son una codificación de los números naturales similares a los naturales Peano-axiomáticos.
0 = λ f x. x No application
1 = λ f x. f x One application
2 = λ f x. f (f x) Twofold
3 = λ f x. f (f (f x)) Threefold
. . .
SUCC = λ n f x. f (n f x) Successor
ADD = λ n m f x. n f (m f x) Addition
MULT = λ n m f x. n (m f) x Multiplication
. . .
Una prueba formal de que
1 + 2 = 3
usando la regla de reescritura de reducción beta:
ADD 1 2
= (λ n m f x. n f (m f x)) (λ g y. g y) (λ h z. h (h z))
= (λ m f x. (λ g y. g y) f (m f x)) (λ h z. h (h z))
= (λ m f x. (λ y. f y) (m f x)) (λ h z. h (h z))
= (λ m f x. f (m f x)) (λ h z. h (h z))
= λ f x. f ((λ h z. h (h z)) f x)
= λ f x. f ((λ z. f (f z)) x)
= λ f x. f (f (f x)) Normal form
= 3
Combinadores
En el cálculo lambda, los combinadores son abstracciones que no contienen variables libres. Más simple: I
el combinador de identidad
λ x. x
isomorfo a la función de identidad
id x = x
Dichos combinadores son los operadores primitivos de los cálculos del combinador, como el sistema SKI.
S = λ x y z. x z (y z)
K = λ x y. x
I = λ x. x
La reducción beta no se normaliza fuertemente ; no todas las expresiones reducibles, "redexes", convergen a la forma normal bajo reducción beta. Un ejemplo simple es la aplicación divergente del ω
combinador omega
λ x. x x
a sí mismo:
(λ x. x x) (λ y. y y)
= (λ y. y y) (λ y. y y)
. . .
= _|_ Bottom
Se prioriza la reducción de subexpresiones más a la izquierda ("cabezas"). El orden de aplicación normaliza los argumentos antes de la sustitución, el orden normal no. Las dos estrategias son análogas a la evaluación entusiasta, por ejemplo, C, y la evaluación perezosa, por ejemplo, Haskell.
K (I a) (ω ω)
= (λ k l. k) ((λ i. i) a) ((λ x. x x) (λ y. y y))
diverge bajo la ansiosa reducción beta de orden aplicativo
= (λ k l. k) a ((λ x. x x) (λ y. y y))
= (λ l. a) ((λ x. x x) (λ y. y y))
= (λ l. a) ((λ y. y y) (λ y. y y))
. . .
= _|_
ya que en semántica estricta
forall f. f _|_ = _|_
pero converge bajo una lenta reducción beta de orden normal
= (λ l. ((λ i. i) a)) ((λ x. x x) (λ y. y y))
= (λ l. a) ((λ x. x x) (λ y. y y))
= a
Si una expresión tiene una forma normal, la reducción beta de orden normal la encontrará.
Y
La propiedad esencial del Y
combinador de punto fijo.
λ f. (λ x. f (x x)) (λ x. f (x x))
es dado por
Y g
= (λ f. (λ x. f (x x)) (λ x. f (x x))) g
= (λ x. g (x x)) (λ x. g (x x)) = Y g
= g ((λ x. g (x x)) (λ x. g (x x))) = g (Y g)
= g (g ((λ x. g (x x)) (λ x. g (x x)))) = g (g (Y g))
. . . . . .
La equivalencia
Y g = g (Y g)
es isomorfo a
fix f = f (fix f)
El cálculo lambda sin tipo puede codificar pruebas constructivas arbitrarias sobre funciones generales / μ-recursivas.
FACT = λ n. Y FACT' n
FACT' = λ rec n. if n == 0 then 1 else n * rec (n - 1)
FACT 3
= (λ n. Y FACT' n) 3
= Y FACT' 3
= FACT' (Y FACT') 3
= if 3 == 0 then 1 else 3 * (Y FACT') (3 - 1)
= 3 * (Y FACT') (3 - 1)
= 3 * FACT' (Y FACT') 2
= 3 * if 2 == 0 then 1 else 2 * (Y FACT') (2 - 1)
= 3 * 2 * (Y FACT') 1
= 3 * 2 * FACT' (Y FACT') 1
= 3 * 2 * if 1 == 0 then 1 else 1 * (Y FACT') (1 - 1)
= 3 * 2 * 1 * (Y FACT') 0
= 3 * 2 * 1 * FACT' (Y FACT') 0
= 3 * 2 * 1 * if 0 == 0 then 1 else 0 * (Y FACT') (0 - 1)
= 3 * 2 * 1 * 1
= 6
(Multiplicación retrasada, confluencia)
Para el cálculo lambda sin tipo Churchian, se ha demostrado que existe una infinidad recursivamente enumerable de combinadores de punto fijo además Y
.
X = λ f. (λ x. x x) (λ x. f (x x))
Y' = (λ x y. x y x) (λ y x. y (x y x))
Z = λ f. (λ x. f (λ v. x x v)) (λ x. f (λ v. x x v))
Θ = (λ x y. y (x x y)) (λ x y. y (x x y))
. . .
La reducción beta de orden normal hace que el cálculo lambda sin tipo no extendido sea un sistema de reescritura completo de Turing.
En Haskell, el combinador de punto fijo se puede implementar con elegancia
fix :: forall t. (t -> t) -> t
fix f = f (fix f)
La pereza de Haskell se normaliza a una finidad antes de que se hayan evaluado todas las subexpresiones.
primes :: Integral t => [t]
primes = sieve [2 ..]
where
sieve = fix (\ rec (p : ns) ->
p : rec [n | n <- ns
, n `rem` p /= 0])