Continuations (MIT/GNU Scheme 10.1.6)

Next: Application Hooks, Previous: Primitive Procedures, Up: Procedures [Contents][Index]

### 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 state 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:

  - The dynamic extent is entered when execution of the body of the called procedure begins.
  - The dynamic extent is also entered when execution is not within the dynamic extent and a continuation is invoked that was captured (using `call-with-current-continuation`) during the dynamic extent.
  - It is exited when the called procedure returns.
  - It is also exited when execution is within the dynamic extent and a continuation is invoked that was captured while not within the dynamic extent.

  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.

Next: Application Hooks, Previous: Primitive Procedures, Up: Procedures [Contents][Index]