Continuations libraries

[!Library] (sagittarius continuations) 0.9.15

This library provides continuation enhancement. The main capabilities this library provides are:

Continuation Predicates

Returns #t if the given obj is a continuation object, otherwise #f.

NOTE: a continuation object is also a procedure.

Returns #t if the given obj is a composable continuation object, otherwise #f.

If the obj is a composable continuation, then it is also a continuation object.

Continuation Prompts

Installs a new prompt into the current continuation frame with the given tag and abort-handler, then calls the proc with the args.

If the tag is not specified, then (default-continuation-prompt-tag) is used.
If the abort-handler is not specified or #f, then default abort handler is used. The default abort handler accepts one argument, which must be a thunk. It's approximately the same as like this:

(call-with-continuation-prompt thunk tag #f)

Returns if the prompt with tag is available in the continuation.

If cont is #f or omitted, then it checks the current continuation. Otherwise, cont must satisfy continuation?, then checks the given continuation.

Aborts the current continuation up until the specified prompt tag tag. Then invokes the abort handler of the prompt.

If the prompt is not found, then it raises an error.

The rest arguments args will be passed to the abort handler.

Prompt Tags

Returns #t if the given obj is a continuation prompt tag.

Currently, continuation prompt tag is implemented with a list, however this may change in the future. Users shouldn't depend on it.

Returns a newly created continuation prompt tag.

If name is specified, then it uses the name as the prompt tag name. Otherwise generates one.

Returns a default continuation prompt tag.

Composable and Delimited Continuations

Captures the current continuation up to the prompt specified by the given tag.

Unlike the continuation of call/cc, invoking composable continuation doesn't abort the current continuation, but it simply extends the current one, executes the captured continuation and returns the invocation point. For example, the script below shows 1,3,2,3 instead of 1,3

(call-with-continuation-prompt
 (lambda ()
   (call-with-composable-continuation
    (lambda (k)
      (display "1,")
      (k 1)
      (display "2")))
   (display "3,")))

If the tag is not specified, then (default-continuation-prompt-tag) is used.

If the prompt is not found, then it raises an error.

Similar to call-with-current-continuation, but only captures the continuation up until the given tag.

Unlike call-with-composable-continuation, invoking the captured continuation aborts the current continuation up to the enclosing prompt, like a standard call/cc continuation.

Invokes proc after installing the continuation k. The proc is called with args.

Unlike directly invoking a continuation, call-in-continuation allows dynamic-wind post thunks to be executed and continuation marks to be accessed in the installed continuation context.

(+ 1
   (call/cc (lambda (k)
              (let ([n 0])
                (dynamic-wind
                    values
                    (lambda ()
                      ;; n is accessed after post thunk runs
                      (call-in-continuation k (lambda () n)))
                    (lambda ()
                      (set! n 4)))))))
;; => 5

Continuation Barrier

Installs a continuation barrier and calls thunk.

A continuation barrier prevents jumps into more deeply nested active procedure calls:

((call-with-continuation-barrier
  (lambda ()
    (call/cc values))))

If a continuation is captured outside of the barrier and escaping from the thunk, it's okay

(call/cc
  (lambda (k)
    (call-with-continuation-barrier
      (lambda ()
        (k 'ok)))))

Continuation Conditions

Condition Type &continuation

Continuation violation condition. This condition is raised when a delimited continuation or composable continuation operation fails because the specified prompt tag is not found.

make-continuation-violation creates a continuation violation condition with the given tag.

continuation-violation? returns #t if obj is a continuation violation condition.

continuation-violation-prompt-tag returns the prompt tag from the condition.

Continuation Marks

Continuation marks provide a mechanism to attach key-value pairs to the call stack. These marks survive across continuation captures and can be queried to examine the dynamic context.

Evaluates expr ... with a continuation mark associating key with value attached to the current continuation frame.

If the immediate context already has a mark for key, the new value replaces it.

Evaluates expr ... with multiple continuation marks attached to the current continuation frame. This is equivalent to nesting multiple with-continuation-mark forms in tail position.

(with-continuation-marks ([key1 'val1]
                          [key2 'val2])
  (list
    (continuation-mark-set->list #f key1)
    (continuation-mark-set->list #f key2)))
;; => ((val1) (val2))

Returns #t if obj is a continuation mark set, otherwise #f.

Returns the continuation mark set for the current continuation up to the nearest prompt with the given tag.

Returns a list of values associated with key in mark-set, from the most recent to the oldest.

If mark-set is #f, uses (current-continuation-marks tag).

(with-continuation-mark 'key 'outer
  (list (with-continuation-mark 'key 'inner
          (continuation-mark-set->list #f 'key))))
;; => ((inner outer))

Similar to continuation-mark-set->list, but returns a list of vectors. Each vector contains the values for all keys in a single continuation frame that has at least one of the keys. If a key is not present in a frame, default is used.

(let ([key1 (make-continuation-mark-key)]
      [key2 (make-continuation-mark-key)])
  (with-continuation-marks ([key1 'val1]
                            [key2 'val2])
    (continuation-mark-set->list* #f (list key1 key2))))
;; => (#(val1 val2))

Returns the value associated with key in the first frame of mark-set that has such an association, or default if none is found.

If mark-set is #f, uses (current-continuation-marks tag).

Returns an iterator procedure over the continuation mark set. Each call to the iterator returns two values: the current vector of values (like from continuation-mark-set->list*) and the next iterator procedure. When exhausted, the first value is #f.

Calls proc with the value associated with key in the immediate continuation frame, or default if no such mark exists.

If the call is not in tail position relative to a with-continuation-mark form with the given key, default is passed to proc.

(with-continuation-mark 'key 'mark
  (call-with-immediate-continuation-mark 'key values))
;; => mark

(with-continuation-mark 'key 'mark
  (list (call-with-immediate-continuation-mark 'key values)))
;; => (#f)  ; not in tail position

Returns a new continuation mark key. Keys created with this function are guaranteed to be unique and can be used as keys in continuation marks.

If name is provided, it is used for display purposes.

Returns #t if obj is a continuation mark key created by make-continuation-mark-key, otherwise #f.

Delimited Control Operators

The following macros provide convenient syntax for common delimited continuation patterns. They are based on the shift/reset and control/prompt paradigms.

Installs a prompt and evaluates expr .... reset uses the default prompt tag, while reset-at uses the specified tag.

Captures the current continuation up to the nearest reset (or reset-at with matching tag) as k, then aborts to that prompt and evaluates expr ....

When k is invoked, it reinstalls a prompt before applying the captured continuation, making the continuation composable.

(+ 1 (reset (* 2 (shift k (k 4)))))
;; => 9  ; (+ 1 (* 2 4))

(+ 1 (reset (* 2 (shift k (k (k 4))))))
;; => 17 ; (+ 1 (* 2 (* 2 4)))

Similar to reset and reset-at, but uses an abort handler that calls the thunk returned by control.

Similar to shift and shift-at, but does not reinstall a prompt when k is invoked. This means invoking k captures up to the enclosing prompt, not the current position.

(prompt (+ 2 (control k (k 5))))
;; => 7

(prompt (+ 2 (control k 5)))
;; => 5