log2 n = min k. | 2k - n | = 0and then transformed into a lambda term by applying the technique seen in previous lecture. Note that it requires the exponenziation and distance (|.|) functions. These can be defined by primitive recursion and then transformed into lambda terms as usual. In particular, the definition of distance can be given as follows:
distance(0, n) = n distance(m+1, n) = if n = 0 then m+1 else distance(m, n-1)or alternatively:
distance(m,n) = if m = 0 then n else if n = 0 then m else distance(m-1,n-1)
E1 = E2where 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.
x = (2*x3 - 6) / 5has solution x = 2, in fact
2 = (2*23 - 6) / 5 holds.
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) / 5Equations 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:
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.
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 MIf 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 MNow use one further abstraction step on the left, and obtain
M = (\u p. p u) MThe latter is in the format of a fixpoint equation. Then we have that a solution is
M =def= Y (\u p. p u)
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 fwhere 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 solutionThis 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) = 1and
f1(n) = undefined for n>0
f2(0) = 1So, a recursive definition of this kind (namely, with several solutions), is ambiguous, at least in principle.
f2(n) = 42 for n>0
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)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.
= 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)
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]  = ([F] ([f]))  = ([F] ([F] ([f])))  = ([F] ([F] ([F] ([f]))))  ...because
Y [F]  = ([F] (Y [F]))  = ([F] ([F] (Y [F])))  = ([F] ([F] ([F] (Y [F]))))  ...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].
f = F f gUsing pairs, this system of equations can be transformed into the single equation
g = G f g
(f,g) = (F f g, G f g)We need however to write it in the fixpoint format. This can be done as follows:
(f,g) = (\p. (F (First p) (Second p), G (First p) (Second p)) ) (f,g)Or, more in general
X = (\p. (F (First p) (Second p), G (First p) (Second p)) ) XNote that the fixpoint of (\p. (F (First p) (Second p), G (First p) (Second p)) ) will necessarily be a pair, because that's what the result of this function is, hence we don't need to impose that X is a pair.
The lambda representation of the solution of this equation is therefore
[X] =def= Y (\p. [Pair] ([F] ([First] p) ([Second] p)) ( [G] ([First] p) ([Second] p)) )
Note that we can separate the components of [X] by using [First] and [Second]. Namely, [First][X] will give [f] and [Second][X] will give [g].