Récursivité anonyme
Un combinateur à virgule fixe est une fonction d'ordre supérieur fix
qui, par définition, satisfait l'équivalence
forall f. fix f = f (fix f)
fix f
représente une solution x
à l'équation à virgule fixe
x = f x
La factorielle d'un nombre naturel peut être prouvée par
fact 0 = 1
fact n = n * fact (n - 1)
L'utilisation de fix
preuves constructives arbitraires sur des fonctions générales / μ-récursives peut être dérivée sans auto-référentialité non-homogène.
fact n = (fix fact') n
où
fact' rec n = if n == 0
then 1
else n * rec (n - 1)
tel 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
Cette preuve formelle que
fact 3 = 6
utilise méthodiquement l'équivalence du combinateur à virgule fixe pour les réécritures
fix fact' -> fact' (fix fact')
Calcul lambda
Le formalisme lambda calcul non typé consiste en une grammaire sans contexte
E ::= v Variable
| λ v. E Abstraction
| E E Application
où v
s'étend sur les variables, ainsi que les règles de réduction bêta et ETA
(λ x. B) E -> B[x := E] Beta
λ x. E x -> E if x doesn’t occur free in E Eta
La réduction bêta remplace toutes les occurrences libres de la variable x
dans le corps d'abstraction («fonction») B
par l'expression («argument») E
. La réduction Eta élimine l'abstraction redondante. Il est parfois omis du formalisme. Une expression irréductible , à laquelle aucune règle de réduction ne s'applique, est sous forme normale ou canonique .
λ x y. E
est un raccourci pour
λ x. λ y. E
(abstraction multiarité),
E F G
est un raccourci pour
(E F) G
(application associative gauche),
λ x. x
et
λ y. y
sont équivalents alpha .
L'abstraction et l'application sont les deux seules «primitives de langage» du calcul lambda, mais elles permettent le codage de données et d'opérations arbitrairement complexes.
Les chiffres de l'Église sont un codage des nombres naturels similaires aux naturels peano-axiomatiques.
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
. . .
Une preuve formelle que
1 + 2 = 3
en utilisant la règle de réécriture de la réduction bêta:
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
Combinateurs
Dans le calcul lambda, les combinateurs sont des abstractions qui ne contiennent aucune variable libre. Plus simplement I
:, le combinateur d'identité
λ x. x
isomorphe à la fonction d'identité
id x = x
De tels combinateurs sont les opérateurs primitifs des calculs de combinateurs comme le système SKI.
S = λ x y z. x z (y z)
K = λ x y. x
I = λ x. x
La réduction bêta ne normalise pas fortement ; toutes les expressions réductibles, «redexes», convergent vers la forme normale sous réduction bêta. Un exemple simple est l'application divergente du ω
combinateur oméga
λ x. x x
à lui-même:
(λ x. x x) (λ y. y y)
= (λ y. y y) (λ y. y y)
. . .
= _|_ Bottom
La réduction des sous-expressions les plus à gauche («têtes») est prioritaire. L'ordre d'application normalise les arguments avant la substitution, contrairement à l' ordre normal . Les deux stratégies sont analogues à une évaluation enthousiaste, par exemple C, et à une évaluation paresseuse, par exemple Haskell.
K (I a) (ω ω)
= (λ k l. k) ((λ i. i) a) ((λ x. x x) (λ y. y y))
diverge sous la réduction bêta de l'ordre d'application
= (λ 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))
. . .
= _|_
car en sémantique stricte
forall f. f _|_ = _|_
mais converge sous une réduction bêta paresseuse d'ordre normal
= (λ l. ((λ i. i) a)) ((λ x. x x) (λ y. y y))
= (λ l. a) ((λ x. x x) (λ y. y y))
= a
Si une expression a une forme normale, une réduction bêta d'ordre normal la trouvera.
Oui
La propriété essentielle du Y
combinateur à virgule fixe
λ f. (λ x. f (x x)) (λ x. f (x x))
est donné par
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))
. . . . . .
L'équivalence
Y g = g (Y g)
est isomorphe à
fix f = f (fix f)
Le calcul lambda non typé peut coder des preuves constructives arbitraires sur des fonctions générales / μ-récursives.
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
(Multiplication retardée, confluence)
Pour le calcul lambda non typé de l'Église, il a été démontré qu'il existe en outre une infinité récursivement énumérable de combinateurs à virgule fixe 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 réduction bêta d'ordre normal fait du calcul lambda non étendu non étendu un système de réécriture complet de Turing.
À Haskell, le combinateur à virgule fixe peut être mis en œuvre avec élégance
fix :: forall t. (t -> t) -> t
fix f = f (fix f)
La paresse de Haskell se normalise à une fin avant que toutes les sous-expressions aient été évaluées.
primes :: Integral t => [t]
primes = sieve [2 ..]
where
sieve = fix (\ rec (p : ns) ->
p : rec [n | n <- ns
, n `rem` p /= 0])