Fall 2000, CSE 520: Lecture 5

The theorem of Kleene (continued)

Encoding of minimalization

Given the definition of f as
f(n1,...,nk) = mu n. (g(n,n1,...,nk) = 0)
we can rewrite it in ML as follows:
val rec h = fn n => fn n1 => ... fn nk => if g(n,n1,...,nk) = 0 then n else h (n+1) n1...nk
val f(n1,...,nk) = h 0 n1...nk
We apply now the method seen for primitive recursion (which works for every kind of recursive definition): Define H as
H = fn h' => fn n => fn n1 => ... fn nk => if g(n,n1,...,nk) = 0 then n else h' (n+1) n1...nk
Then we can define
[h] = Y [H]
[f] = [h][0]

Note: In the classical proof of the Klenee's theorem, the encoding of primitive recursion does not use the fixpoint operator. The fixpoint operator in fact is necessary for expressing minimalization (and general recursion), but not for primitive recursion. The classic encoding of primitive recursion is done by using "definite iteration" (i.e. iteration repeated a number of times known a-priori) and pairs to store the partial result of the computation. We give here the translation in ML and we leave the translation from ML to the Lambda Calculus as an exercise.

Let f be defined by primitive recursion as:

f(0) = g   
f(n+1) = h(n,f(n))
then f can be defined in pseudo-ML by the following declarations:
fun phi(m,n) = (m+1,h(m,n))
fun f(n) = second (phin (0,g))
Where second is the projection on the second element of a pair, i.e. second (a,b) = b. To see that the above is a correct definition, note that phi(m,f(m)) = (m+1,f(m+1)), hence phin (0,g) = (n,f(n)).

In order to translate from ML to the Lambda Calculus, we need to encode pairs and the projection operators like we did for the predecessor function. As for definite iteration, needed for representing phin, remember that in the Lambda Calculus we have MnN = [n] M N. Hence we can define

[f] = \z. Second (z [phi] (Pair [0] [g]))

Recursive definitions and fixpoints

In this section we review the concepts of recursive definition and fixpoint operator, and try to give some further explanation about them. Let us start with some basic definition.

Equations and solutions of equations

Given an equality theory, an equation on x is a formula of the form
E1 = E2
where E1 are E2 are expressions containing some (zero, one, or several) occurrences of x.

A solution for the above equation is any value v such that

E1[v/x] = E2[v/x]   holds (i.e. this equality is a consequence of the theory)
where [v/x] represents the substitution of v for x. Of course, an equation can have zero, one, or several solutions.


In the theory of rational numbers, the equation
x = (2*x3 - 6) / 5
has solution x = 2, in fact
2 = (2*23 - 6) / 5 holds.

Recursive definitions

The equation of the example above is in a particular format: it has the form

x = f(x)
where f is the function defined as
f(y) =def= (2 y3 - 6) / 5
Equations in this format are called ``fixpoint equations'' and also ``recursive definitions''. The latter terminology is a bit misleading, because it leads to think that such equation is a definition of x. The idea indeed is that x is defined, by such equation, as the solution of the equation. However:
  1. The equation may not have any solution. Example: x = x+1 has no solution in the natural numbers.
  2. The equation may have more than one solution. Example: x = x2 has two solutions: x = 0 and x = 1.
Only in the ``lucky case'' that x = f(x) has one and only one solution, then we can consider such equation a real definition of x.
Note: The solutions of x = f(x) are called ``fixpoints of f''.

For certain theories, the solution of fixpoint equations can be obtained in a uniform way, by applying an operator to the function f of the equation. Such operator is called fixpoint operator.

The fixpoint operators in lambda calculus

The theory of lambda calculus has fixpoint operators. One fixpoint operator is the lambda term Y defined in previous lectures. In fact, as previously proved, we have
Y F = F (Y F)
for every lambda term F. This means that every fixpoint equation
X = F (X)
has a solution X =def= Y F.

Note that Y is not the only fixpoin operator in the lambda calculus. We have, actually, infinitely many such operators. The operator Y is due to Curry, and was called by him "paradoxical combinator". Another fixpoint operator, due to Turing, is the term (\x y. y (x x y))(\x y. y (x x y)).

Example Suppose that we want to find a term M such that, for every P, we have:

M P = P M
If we are able to reduce the above equation to a fixpoint equation, then we can use the fixpoint operator to solve this problem.

Observe that the above equation is equivalent to

M = \p. p M
Now use one further abstraction step on the left, and obtain
M = (\u p. p u) M
The latter is in the format of a fixpoint equation. Then we have that a solution is
M =def= Y (\u p. p u)

Recursive definitions of functions

Consider a recursive definition of a (partial) function f, like for instance
f(n)   =def=   if n = 0 then ... else ... f(n-1) ...
or equivalently
f   =def=   \n. if n = 0 then ... else ... f(n-1) ...
what we mean by such a definition is that we want to define f as the solution of a fixpoint equation on f of the form
f = F f
where F is the function defined as
F   =def=   (\u. \n. if n = 0 then ... else ... u(n-1) ...)
The recursive definitions (fixpoint equations) of functions enjoy an important property, namely
In the domain of functions, a fixpoint equation f = F f admits always at least one solution
This is because we are considering partial functions, namely functions which can be undefined on any argument.

There may be, hoverer, more than one solution. For example, consider the following recursive definition:

f(n) = if n = 0 then 1 else f(n+1)
There are infinitely many solutions for this equations. All of them must return 1 in correspondence of 0, but, on the other arguments, there is complete freedom. For instance, the following functions are solutions of the above equation:
f1(0) = 1
f1(n) = undefined    for n>0
f2(0) = 1
f2(n) = 42   for n>0
So, a recursive definition of this kind (namely, with several solutions), is ambiguous, at least in principle.

In practive however, when we write in a a recursive definition of this kind in a program, there is no ambiguity: the operational semantics of the language ensures that the intended solution is uniquely defined. Usually, such intended solution is the most partial function (the function wich returns ``undefined'' most often). In the example above, the intended solution would be f1. The reason why we get the most undefined solution is that the operational semantics of recursive definitions is (usually) based on unfolding. For example, a call of f(5), where f is defined as above, gives rise to the following sequence of unfoldings:

f(5) = if 5 = 0 then 1 else f(6)
     = if 5 = 0 then 1 else if 6 = 0 then 1 else f(7)
     = if 5 = 0 then 1 else if 6 = 0 then 1 else if 7 = 0 then 1 else f(8)
     = ...
So, if the test for termination is on 0, recursive calls on non-decreasing arguments will give rise to non-terminating loops, and therefore be undefined.

More in general, a call f(k) where f = F f will give rise to a sequence of the form

f(5) = (F f) 5 = (F (F f)) 5 = (F (F (F f))) 5 = ...
Note that the fixpoint operator of the lambda calculus also fives this kind of behavior: if we define [f] = Y [F], we have:
[f] [5] = ([F] ([f])) [5] = ([F] ([F] ([f]))) [5] = ([F] ([F] ([F] ([f])))) [5] ...
Y [F] [5] = ([F] (Y [F])) [5] = ([F] ([F] (Y [F]))) [5] = ([F] ([F] ([F] (Y [F])))) [5] ...
Therefore the definition of [f] as Y [F] corresponds to take as solution of   f = F f   the most partial one (Provided of course that the functions used in the definition. of F are represented faithfully with respect to partiality.) This means that, if we call f' such most partial solution, we have that: if f'(n) is defined then [f] [n] = [f'(n)], and if f'(n) is not defined then there exists no k such that [f] [n] = [k].