10 Continuations

A continuation is a plan for all future computation, parameterized by a value to be provided, and contingent on the states of all mutable data structures (which notably may include environments). When the Kernel evaluator is invoked, the invoker provides a continuation to which the result of the evaluation will normally be returned.

For example, when $if evaluates its test operand, the continuation provided for the result expects to be given a boolean value; and, depending on which boolean it gets, it will evaluate either the consequent or the alternative operand as a tail context — that is, the continuation provided for the result of evaluating the selected operand is the same continuation that was provided for the result of the call to $if.

A Kernel program may sometimes capture a continuation; that is, acquire a reference to it as a first-class object. The basic means of continuation capture is applicative call/cc. Given a first-class continuation c, a combiner can be constructed that will abnormally pass its operand tree to c (as opposed to the normal return of values to continuations). In the simplest case, the abnormally passed value arrives at c as if it had been normally returned to c. In general, continuations bypassed by the abnormal pass may have entry/exit guards attached to them, and these guards can intercept the abnormal pass before it reaches c. Each entry/exit guard consists of a selector continuation, which designates which abnormal passes the guard will intercept, and an interceptor applicative that performs the interception when selected.

Continuations are immutable, and are equal? iff eq?. The continuation type is encapsulated.

— Applicative: continuation? (continuation? . objects)

The primitive type predicate for type continuation. continuation? returns true iff all the objects in objects are of type continuation.

— Applicative: call/cc (call/cc combiner)

Calls combiner in the dynamic environment as a tail context, passing as sole operand to it the continuation to which call/cc would normally return its result. (That is, constructs such a combination and evaluates it in the dynamic environment.)

— Applicative: extend-continuation (extend-continuation continuation applicative [environment])

The extend-continuation applicative constructs and returns a new child of continuation that, when it normally receives a value v, calls the underlying combiner of applicative with dynamic environment environment (or an empty environment if none was specified) and operand tree v, the result of the call normally to be returned to continuation.

The following equivalnece defines the short version:

          (extend-continuation c a) ==
            (extend-continuation c a (make-environment))
— Applicative: guard-continuation (guard-continuation entry-guards continuation exit-guards)

entry-guards and exit-guards should each be a list of clauses; each clause should be a list of length two, whose first element is a continuation, and whose second element is an applicative whose underlying combiner is operative.

Applicative guard-continuation constructs two continuations: a child of continuation, called the outer continuation; and a child of the outer continuation, called the inner continuation. The inner continuation is returned as the result of the call to guard-continuation.

When the inner continuation normally receives a value, it passes the value normally to the outer continuation; and when the outer continuation normally receives a value, it passes the value normally to continuation. Thus, in the absence of abnormal passing, the inner and outer continuations each have the same behavior as continuation.

The two elements of each guard clause are called, respectively, the selector and the interceptor. The selector continuation is used in deciding whether to intercept a given abnormal pass, and the interceptor applicative is called to perform customized action when interception occurs.

At the beginning of the call to guard-continuation, internal copies are made of the evaluation structures of entry-guards and exit-guards, so that the selectors and interceptors contained in the arguments at that time remain fixed thereafter, independent of any subsequent mutations to the arguments.

— Applicative: continuation->applicative (continuation->applicative continuation)

Returns an applicative whose underlying operative abnormally passes its operand tree to continuation, thus: A series of interceptors are selected to handle the abnormal pass, and a continuation is derived that will normally perform all the interceptions in sequence and pass some value to the destination of the originally abnormal pass. The operand tree is then normally passed to the derived continuation.

— Variable: root-continuation

This continuation is the ancestor of all other continuations. When it normally receives a value, it terminates the Kernel session. (For example, if the system is running a read-eval-print loop, it exits the loop.)

— Variable: error-continuation

The dynamic extent of this continuation is mutually disjoint from the dynamic extent in which Kernel computation usually occurs (such as the dynamic extent in which the Kernel system would run a read-eval-print loop).

When this continuation normally receives a value, it provides a diagnostic message to the user of the Kernel system, on the assumption that the received value is an attempt to describe some error that aborted a computation; and then resumes operation of the Kernel system at some point that is outside of all user-defined computation. (For example, if the system is running a read-eval-print loop, operation may resume by continuing from the top of the loop.)

The diagnostic message is not made available to any Kernel computation, and is therefore permitted to contain information that violates abstractions within the system.

When an error is signaled during a Kernel computation, the signaling action consists of an abnormal pass to some continuation in the dynamic extent of error-continuation.

— Applicative: apply-continuation (apply-continuation continuation object)

Applicative apply-continuation converts its first argument to an applicative as if by continuation->applicative, and then applies it as usual.

That is:

          (apply-continuation continuation object) ==
            (apply (continuation->applicative continuation) object)
— Operative: $let/cc ($let/cc <symbol> . <objects>)

A child environment e of the dynamic environment is created, containing a binding of <symbol> to the continuation to which the result of the call to $let/cc should normally return; then, the subexpressions of <objects> are evaluated in e from left to right, with the last (if any) evaluated as a tail context, or if <objects> is empty the result is inert.

That is:

          ($let/cc symbol . objects) ==
            (call/cc ($lambda (symbol) . objects))
— Applicative: guard-dynamic-extent (guard-dynamic-extent entry-guards combiner exit-guards)

This applicative extends the current continuation with the specified guards, and calls combiner in the dynamic extent of the new continuation, with no operands and the dynamic environment of the call to guard-dynamic-extent.

— Applicative: exit (exit [object])

Applicative exit initiates an abnormal transfer of object (or #inert if object was not specified), to root-continuation. That is:

          (exit) == (apply-continuation root-continuation #inert)
          (exit obj) == (apply-continuation root-continuation obj)

SOURCE NOTE: This applicative doesn’t have the optional argument in the report. It was added to klisp to allow a simple way to terminate the interpreter passing a value that is then tried to convert to an exit status.