Next: , Previous: Primitive Procedures, Up: Procedures


12.4 Continuations

— procedure: call-with-current-continuation procedure

Procedure must be a procedure of one argument. Packages up the current continuation (see below) as an escape procedure and passes it as an argument to procedure. The escape procedure is a Scheme procedure of one argument that, if it is later passed a value, will ignore whatever continuation is in effect at that later time and will give the value instead to the continuation that was in effect when the escape procedure was created. The escape procedure created by call-with-current-continuation has unlimited extent just like any other procedure in Scheme. It may be stored in variables or data structures and may be called as many times as desired.

The following examples show only the most common uses of this procedure. If all real programs were as simple as these examples, there would be no need for a procedure with the power of call-with-current-continuation.

          (call-with-current-continuation
            (lambda (exit)
              (for-each (lambda (x)
                          (if (negative? x)
                              (exit x)))
                        '(54 0 37 -3 245 19))
              #t))                                =>  -3
          
          (define list-length
            (lambda (obj)
              (call-with-current-continuation
                (lambda (return)
                  (letrec ((r
                            (lambda (obj)
                              (cond ((null? obj) 0)
                                    ((pair? obj) (+ (r (cdr obj)) 1))
                                    (else (return #f))))))
                    (r obj))))))
          (list-length '(1 2 3 4))                =>  4
          (list-length '(a b . c))                =>  #f
     

A common use of call-with-current-continuation is for structured, non-local exits from loops or procedure bodies, but in fact call-with-current-continuation is quite useful for implementing a wide variety of advanced control structures.

Whenever a Scheme expression is evaluated a continuation exists that wants the result of the expression. The continuation represents an entire (default) future for the computation. If the expression is evaluated at top level, for example, the continuation will take the result, print it on the screen, prompt for the next input, evaluate it, and so on forever. Most of the time the continuation includes actions specified by user code, as in a continuation that will take the result, multiply it by the value stored in a local variable, add seven, and give the answer to the top-level continuation to be printed. Normally these ubiquitous continuations are hidden behind the scenes and programmers don't think much about them. On the rare occasions that you may need to deal explicitly with continuations, call-with-current-continuation lets you do so by creating a procedure that acts just like the current continuation.

— procedure: continuation? object

Returns #t if object is a continuation; otherwise returns #f.

— procedure: within-continuation continuation thunk

Thunk must be a procedure of no arguments. Conceptually,
within-continuation invokes continuation on the result of invoking thunk, but thunk is executed in the dynamic context of continuation. In other words, the “current” continuation is abandoned before thunk is invoked.

— procedure: dynamic-wind before thunk after

Calls thunk without arguments, returning the result(s) of this call. Before and after are called, also without arguments, as required by the following rules (note that in the absence of calls to continuations captured using call-with-current-continuation the three arguments are called once each, in order). Before is called whenever execution enters the dynamic extent of the call to thunk and after is called whenever it exits that dynamic extent. The dynamic extent of a procedure call is the period between when the call is initiated and when it returns. In Scheme, because of call-with-current-continuation, the dynamic extent of a call may not be a single, connected time period. It is defined as follows:

If a second call to dynamic-wind occurs within the dynamic extent of the call to thunk and then a continuation is invoked in such a way that the afters from these two invocations of dynamic-wind are both to be called, then the after associated with the second (inner) call to dynamic-wind is called first.

If a second call to dynamic-wind occurs within the dynamic extent of the call to thunk and then a continuation is invoked in such a way that the befores from these two invocations of dynamic-wind are both to be called, then the before associated with the first (outer) call to dynamic-wind is called first.

If invoking a continuation requires calling the before from one call to dynamic-wind and the after from another, then the after is called first.

The effect of using a captured continuation to enter or exit the dynamic extent of a call to before or after is undefined.

          (let ((path '())
                (c #f))
            (let ((add (lambda (s)
                         (set! path (cons s path)))))
              (dynamic-wind
                (lambda () (add 'connect))
                (lambda ()
                  (add (call-with-current-continuation
                         (lambda (c0)
                           (set! c c0)
                           'talk1))))
                (lambda () (add 'disconnect)))
              (if (< (length path) 4)
                  (c 'talk2)
                  (reverse path))))
          
          => (connect talk1 disconnect connect talk2 disconnect)
     

The following two procedures support multiple values.

— procedure: call-with-values thunk procedure

Thunk must be a procedure of no arguments, and procedure must be a procedure. Thunk is invoked with a continuation that expects to receive multiple values; specifically, the continuation expects to receive the same number of values that procedure accepts as arguments. Thunk must return multiple values using the values procedure. Then procedure is called with the multiple values as its arguments. The result yielded by procedure is returned as the result of call-with-values.

— procedure: values object ...

Returns multiple values. The continuation in effect when this procedure is called must be a multiple-value continuation that was created by call-with-values. Furthermore it must accept as many values as there are objects.