Next: Breaks Up: Exceptions and Control Flow Previous: Dynamic Wind

# Continuation Marks

To evaluate a sub-expression, MzScheme creates a continuation for the sub-expression that extends the current continuation. For example, to evaluate expr in the expression ``

      (begin
        expr
        expr  )

MzScheme extends the continuation of the `begin` expression with one continuation frame to create the continuation for expr . In contrast, expr is in tail position for the `begin` expression, so its continuation is the same as the continuation of the `begin` expression.

A continuation mark is a keyed mark in a continuation frame. A program can install a mark in the first frame of its current continuation, and it can extract the marks from all of the frames in its current continuation. Continuation marks support debuggers and other program-tracing facilities, because continuation frames roughly correspond to stack frames in traditional languages. For example, a debugger can annotate a source program to store continuation marks that relate each expression to its source location; when an exception occurs, the marks are extracted from the current continuation to produce a ``stack trace'' for the exception.

The list of continuation marks for a key k and a continuation C that extends C is defined as follows:

- If C's first frame contains a mark m for k, then the mark list for C is `(cons m l )`, where l is the mark list for k in C .
- If C's first frame does not contain a mark keyed by k, then the mark list for C is the mark list for C .

The mark list for the empty continuation is `null` for all keys.

The `with-continuation-mark` form installs a mark on the first frame of the current continuation: ``

      (with-continuation-mark key-expr mark-expr
        body-expr)

The key-expr, mark-expr, and body-expr expressions are evaluated in order. After key-expr is evaluated to obtain a key and mark-expr is evaluated to obtain a mark, the key is mapped to the mark in the current continuation's initial frame. If the frame already has a mark for the key, it is replaced. Finally, the body-expr is evaluated; the continuation for evaluating body-expr is the continuation of the `with-continuation-mark` expression (so the result of the body-expr is the result of the `with-continuation-mark` expression, and body-expr is in tail position for the `with-continuation-mark` expression).

The `current-continuation-marks` procedure extracts the complete set of continuation marks from the current continuation:

- `(current-continuation-marks)` returns a newly-created (opaque) value containing the set of continuation marks for all keys in the current continuation.

The `continuation-mark-set->list` procedure extracts mark values for a particular key from a continuation mark set:

- `(continuation-mark-set->list mark-set key-v)` returns a newly-created list containing the marks for key-v in mark-set, which is a set of marks returned by `current-continuation-marks`.
- `(continuation-mark-set? v)` returns `#t` if v is a mark set created by `current-continuation-marks`, `#f` otherwise.

Examples: ``

      (define (extract-current-continuation-marks key)
         (continuation-mark-set->list
          (current-continuation-marks)
          key))

      (with-continuation-mark 'key 'mark
        (extract-current-continuation-marks 'key)) ; => '(mark)

      (with-continuation-mark 'key1 'mark1
        (with-continuation-mark 'key2 'mark2
          (list
           (extract-current-continuation-marks 'key1)
           (extract-current-continuation-marks 'key2)))) ; => '((mark1) (mark2))

      (with-continuation-mark 'key 'mark1
        (with-continuation-mark 'key 'mark2 ; replaces the previous mark
          (extract-current-continuation-marks 'key)))) ; => '(mark2)

      (with-continuation-mark 'key 'mark1
        (list ; continuation extended to evaluate the argument
         (with-continuation-mark 'key 'mark2
           (extract-current-continuation-marks 'key)))) ; => '((mark1 mark2))

      (let loop ([n 1000])
        (if (zero? n)
            (extract-current-continuation-marks 'key)
            (with-continuation-mark 'key n
              (loop (sub1 n))))) ; => '(1)

In the final example, the continuation mark is set 1000 times, but `extract-current-continuation-marks` returns only one mark value. Because `loop` is called tail-recursively, the continuation of each call to `loop` is always the continuation of the entire expression. Therefore, the `with-continuation-mark` expression replaces the existing mark each time rather than adding a new one.

Whenever MzScheme creates an exception record, it fills the `continuation-marks` field with the value of `(current-continuation-marks)`, thus providing a snapshot of the continuation marks at the time of the exception.

When a continuation procedure returned by `call-with-current-continuation` is invoked, it restores the captured continuation, and also restores the marks in the continuation's frames to the marks that were present when `call-with-current-continuation` was invoked.

Next: Breaks Up: Exceptions and Control Flow Previous: Dynamic Wind