Fall 2000, CSE 520: Lectures 3 and 4


Expressiveness of the Lambda Calculus

We will show now that the lambda calculus is expressive enough to encode all Recursive functions, whose definition we have seen in previous lecture. This result is due to Kleene.

The Theorem of Kleene (1936)

All Recursive functions can be defined in the lambda calculus.

Church's numerals

In order to show the above result, we first need to encode the natutal numbers as lambda terms. The following is a possible representation:
[n] = \x y. xny
where, 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.

Lambda-definability

We now make precise the concept of defining a function in the lambda calculus.

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.

Lambda-definability of the recursive functions

We give here a simpler proof than the original proof by Kleene.

Encoding of the initial functions

It is easy to see that these encodings satisfy the definition of lambda-definability. For instance:
[S][n] = (\z.\x y. x(z x y))[n] = \x y. x([n] x y) = \x y. x(xn y) = [n+1]

Encoding of composition

[f o (g1,...,gk)] = \x1...xh. [f]([g1]x1...xh) ... ([gk]x1...xh)

Encoding of primitive recursion

Assume, for the sake of simplicity, that k = 0 (the general case is left for exercise). The equations defining f in this case are:
f(0) = g    (constant)
f(n+1) = h(n,f(n))
In ML we could define this function as:
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)

The fixpoint operator

We need a lambda term which is able to compute the fixpoint of a function, i.e. a term Y such that for every M it satisfies:
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.

Booleans and the if-then-else operator

Let us encode the booleans as follows:
[true] = \x y. x
[false] = \x y. y
Note that we have:
[true] M N = M
[false] M N = N
Therefore it is sufficient to define
[if_then_else] = \x y z . x y z
In 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].

Test is_zero

We can define
[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.

Predecessor

One way to define the predecessor function is by using the pairing and the projection operators, which can be defined as follows:
Pair = \x y z. z x y
First = \p. p [true]
Second = \p. p [false]
Note 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:
Aux = \x. Pair ([S] (First x)) (First x)
Predecessor = \z. Second (z Aux (Pair [0] [0]))
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.

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 I
where
S2 = \z w. w (z x)
Z2 = \w. y
I = \u. u
We leave to the interested reader to show that also Predecessor' is a correct encoding of the predecessor function.

Note: both Predecessor and Predecessor', when applied to [0], give result [0].