[n] = \x y. xnywhere, for generic lambda terms M and N, MnN represents the term M(...(M(M N))...), i.e. the application of M to N repeated n times. These terms [n] are called Church's numerals.
A partial function f : Nk -> N is lambda-definable if there exists a lambda term [f] such that, whenever f(n1,...,nk) is defined, we have
[f][n1]...[nk] = [f(n1,...,nk)]The equality here is the "lambda-convertibility" (see previous lecture), i.e. the equality in the theory of Lambda Calculus. Note that [f] is curried, i.e. it takes its arguments one by one.
[S][n] = (\z.\x y. x(z x y))[n] = \x y. x([n] x y) = \x y. x(xn y) = [n+1]
[f o (g1,...,gk)] = \x1...xh. [f]([g1]x1...xh) ... ([gk]x1...xh)
f(0) = g (constant)In ML we could define this function as:
f(n+1) = h(n,f(n))
fun f(n) = if n = 0 then g else h(n-1,f(n-1))or equivalently (remember that the syntax for \n. M[n] in ML is fn n => M[n]) :
val rec f = fn n => if n = 0 then g else h(n-1,f(n-1))The meaning of this declaration is that f is a fixpoint of the following functional F:
F = fn f' => fn n => if n = 0 then g else h(n-1,f'(n-1))Namely, f satisfies the equation
f = F(f)
Y M = M (Y M)One possible definition of such Y is
Y = \x. (\y. x(y y)) (\y. x(y y))In fact, Y M = (\y. M(y y)) (\y. M(y y)) = M((\y. M(y y)) (\y. M(y y))) = M (Y M).
We can therefore define
f = Y [F]It remains to define [F]. To this purpose, we need to define the booleans and the if-then-else operator, the test is_zero, and the predecessor.
[true] = \x y. xNote that we have:
[false] = \x y. y
[true] M N = MTherefore it is sufficient to define
[false] M N = N
[if_then_else] = \x y z . x y zIn fact we will have: [if_then_else] C M N = M if C = [true], and [if_then_else] C M N = N if C = [false].
[is_zero] = \x. x ([true][false])[true]In fact, [n] M N = MnN, hence [is_zero][n] = [n]([true][false])[true] = ([true][false])n[true] and the latter is equal (lambda-convertible) to [true] if n=0, and to [false] otherwise.
Pair = \x y z. z x yNote that this definition is a correct encoding, since we have First (Pair M N) = M and Second (Pair M N) = N. Now define the encoding of the predecessor function as follows:
First = \p. p [true]
Second = \p. p [false]
Aux = \x. Pair ([S] (First x)) (First x)Where [S] = \z x y. x (z x y) is the usual encoding of the successor function. To see how the definition works, remember that [n] M N = Mn N.
Predecessor = \z. Second (z Aux (Pair [0] [0]))
Note that the reason why we need pairing is that the Aux function needs returning a pair of values. The pair is a particular case of the concept of record in Pascal and of structure in C/C++.
An alternative definition of the predecessor function, which does not make use of pairs, is the following:
Predecessor' = \z x y. z S2 Z2 Iwhere
S2 = \z w. w (z x)We leave to the interested reader to show that also Predecessor' is a correct encoding of the predecessor function.
Z2 = \w. y
I = \u. u
Note: both Predecessor and Predecessor', when applied to [0], give result [0].