Page:AIM-353.djvu/14




 * 1) In the usual applicative expression there are two implicit temporary values: those of   and  . The first of these values must be preserved over the computation of the second, whereas the second is used as soon as it is produced. These facts are manifest in the appearance and use of the variable   and   in the continuation-passing version.

In short, the continuation-passing version specifies exactly and explicitly what steps are necessary to compute the value of the expression. One can think of conventional functional application for values as being an abbreviation for the more explicit continuation-passing style. Alternatively, one can think of the interpreter as supplying to each function an implicit default continuation of one argument. This continuation will receive the value of the function as its argument, and then carry on the computation. In an interpreter this implicit continuation is represented by the control stack mechanism for function returns.

Now consider what computation steps are implied by

 (LAMBDA (A B C ...) (F X Y Z ...))

When we "apply" the  expression we have some values to apply it to; we let the names ,  ,   ... refer to these values. We then determine the values of,  ,   ... and pass these values (along with "the buck", i.e. control!) to the lambda expression   (  is either a lambda expression or a name for one). Passing control to  is an unconditional transfer. {Note Jrsthack} {Note Hewitthack}

Note that we want values from,  ,   ... If these are simple expressions, such as variables, constants, or   expressions, the evaluation process is trivial, in that no temporary storage is required. In pure continuation-passing style, all evaluations are trivial: no combination is nested within another, and therefore no "hidden temporaries" are required. But if  is a combination, say , then we want to think of   as a function, because we want a value from it, and we will need an implicit temporary place to keep the result while evaluating   and. (An interpreter usually keeps these temporary places in the control stack!) On the other hand, we do not necessarily need a value from  This is what we mean by tail-recursion:   is called tail-recursively, whereas   is not. A better name for tail-recursion would be "tail-transfer", since no real recursion is implied. This is why we have made such a fuss about tail-recursion: it can be used for transfer of control without making any commitment about whether the expression expected to return a value. Thus it can be used to model statement-like control structures. Put another way, tail-recursion does not require a control stack as nested recursion does. In our models of iteration and imperative style all the  expressions used for control (to simulate   statements, for example) are called in tail-recursive fashion.