10.4 Continuations

► |  | The Racket Reference

1 | Language Model
2 | Notation for Documentation
3 | Syntactic Forms
4 | Datatypes
5 | Structures
6 | Classes and Objects
7 | Units
8 | Contracts
9 | Pattern Matching
10 | Control Flow
11 | Concurrency and Parallelism
12 | Macros
13 | Input and Output
14 | Reflection and Security
15 | Operating System
16 | Memory Management
17 | Unsafe Operations
18 | Running Racket
| Bibliography
| Index

▼ | 10 | Control Flow

10.1 | Multiple Values
10.2 | Exceptions
10.3 | Delayed Evaluation
10.4 | Continuations
10.5 | Continuation Marks
10.6 | Breaks
10.7 | Exiting
10.8 | Unreachable Expressions

► | 10.4 | Continuations

10.4.1 | Additional Control Operators

On this page:

call-with-continuation-prompt
abort-current-continuation
make-continuation-prompt-tag
default-continuation-prompt-tag
call-with-current-continuation
call/ cc
call-with-composable-continuation
call-with-escape-continuation
call/ ec
call-in-continuation
let/ cc
let/ ec
call-with-continuation-barrier
continuation-prompt-available?
continuation?
continuation-prompt-tag?
dynamic-wind
10.4.1`` Additional Control Operators
call/ prompt
abort/ cc
call/ comp
abort
%
fcontrol
prompt
control
prompt-at
control-at
reset
shift
reset-at
shift-at
prompt0
reset0
control0
shift0
prompt0-at
reset0-at
control0-at
shift0-at
spawn
splitter
new-prompt
set
cupto

top contents ← prev up next →

#### 10.4`` Continuations🔗ℹ

> > > Continuations in The Racket Guide introduces continuations.

See Sub-expression Evaluation and Continuations and Prompts, Delimited Continuations, and Barriers for general information about continuations. Racket’s support for prompts and composable continuations [Flatt07] closely resembles Sitaram’s % and fcontrol operator [Sitaram93].

Racket installs a continuation barrier around evaluation in the following contexts, preventing full-continuation jumps into the evaluation context protected by the barrier:

- applying an exception handler, an error escape handler, or an error display handler (see Exceptions);

- applying a macro transformer (see Syntax Transformers), evaluating a compile-time expression, or applying a module name resolver (see Resolving Module Names);

- applying a custom-port procedure (see Custom Ports), an event guard procedure (see Events), or a parameter guard procedure (see Parameters);

- applying a security-guard procedure (see Security Guards);

- applying a will procedure (see Wills and Executors); or

- evaluating or loading code from the stand-alone Racket command line (see Running Racket or GRacket).

In addition, extensions of Racket may install barriers in additional contexts. Finally, call-with-continuation-barrier applies a thunk barrier between the application and the current continuation.

> > procedure
> > (call-with-continuation-prompt |  | proc |  |  |  |
> > | [ | prompt-tag |  |  |  |
> > |  | handler] |  |  |  |
> > |  | arg ...) |  | → |  | any
> proc : procedure?
> | prompt-tag |  | : |  | continuation-prompt-tag?
> |  |  | = |  | (default-continuation-prompt-tag)
> handler : (or/c procedure? #f) = #f
> arg : any/c

Applies proc to the given args with the current continuation extended by a prompt. The prompt is tagged by prompt-tag, which must be a result from either default-continuation-prompt-tag (the default) or make-continuation-prompt-tag. The call to call-with-continuation-prompt returns the result of proc.

The handler argument specifies a handler procedure to be called in tail position with respect to the call-with-continuation-prompt call when the installed prompt is the target of an abort-current-continuation call with prompt-tag; the remaining arguments of abort-current-continuation are supplied to the handler procedure. If handler is #f, the default handler accepts a single abort-thunk argument and calls (call-with-continuation-prompt abort-thunk prompt-tag #f); that is, the default handler re-installs the prompt and continues with a given thunk.

> > procedure
> > (abort-current-continuation |  | prompt-tag |  |  |  |
> > |  | v ...) |  | → |  | any
> prompt-tag : any/c
> v : any/c

Resets the current continuation to that of the nearest prompt tagged by prompt-tag in the current continuation; if no such prompt exists, the exn:fail:contract:continuation exception is raised. The vs are delivered as arguments to the target prompt’s handler procedure.

The protocol for vs supplied to an abort is specific to the prompt-tag. When abort-current-continuation is used with (default-continuation-prompt-tag), generally, a single thunk should be supplied that is suitable for use with the default prompt handler. Similarly, when call-with-continuation-prompt is used with (default-continuation-prompt-tag), the associated handler should generally accept a single thunk argument.

Each thread’s continuation starts with a prompt for (default-continuation-prompt-tag) that uses the default handler, which accepts a single thunk to apply (with the prompt intact).

> procedure (make-continuation-prompt-tag) → continuation-prompt-tag?
> (make-continuation-prompt-tag name) → continuation-prompt-tag?
> name : symbol?

Creates a prompt tag that is not equal? to the result of any other value (including prior or future results from make-continuation-prompt-tag). The optional name argument, if supplied, specifies the name of the prompt tag for printing or object-name.

Changed in version 7.9.0.13 of package base: The name argument gives the name of the prompt tag.

> procedure (default-continuation-prompt-tag) → continuation-prompt-tag?

Returns a constant prompt tag for which a prompt is installed at the start of every thread’s continuation; the handler for each thread’s initial prompt accepts any number of values and returns. The result of default-continuation-prompt-tag is the default tag for any procedure that accepts a prompt tag.

> > procedure
> > (call-with-current-continuation |  | proc |  |  |  |
> > | [ | prompt-tag]) |  | → |  | any
> proc : (continuation? . -> . any)
> | prompt-tag |  | : |  | continuation-prompt-tag?
> |  |  | = |  | (default-continuation-prompt-tag)

Captures the current continuation up to the nearest prompt tagged by prompt-tag; if no such prompt exists, the exn:fail:contract:continuation exception is raised. The truncated continuation includes only continuation marks and dynamic-wind frames installed since the prompt.

The captured continuation is delivered to proc, which is called in tail position with respect to the call-with-current-continuation call.

If the continuation argument to proc is ever applied, then it removes the portion of the current continuation up to the nearest prompt tagged by prompt-tag (not including the prompt; if no such prompt exists, the exn:fail:contract:continuation exception is raised), or up to the nearest continuation frame (if any) shared by the current and captured continuations—whichever is first. While removing continuation frames, dynamic-wind post-thunks are executed. Finally, the (unshared portion of the) captured continuation is appended to the remaining continuation, applying dynamic-wind pre-thunks.

The arguments supplied to an applied procedure become the result values for the restored continuation. In particular, if multiple arguments are supplied, then the continuation receives multiple results.

If, at application time, a continuation barrier would be introduced by replacing the current continuation with the applied one, then the exn:fail:contract:continuation exception is raised.

A continuation can be invoked from the thread (see Threads) other than the one where it was captured.

> procedure (call/cc proc [prompt-tag]) → any
> proc : (continuation? . -> . any)
> | prompt-tag |  | : |  | continuation-prompt-tag?
> |  |  | = |  | (default-continuation-prompt-tag)

The call/cc binding is an alias for call-with-current-continuation.

> > procedure
> > (call-with-composable-continuation |  | proc |  |  |  |
> > | [ | prompt-tag]) |  | → |  | any
> proc : (continuation? . -> . any)
> | prompt-tag |  | : |  | continuation-prompt-tag?
> |  |  | = |  | (default-continuation-prompt-tag)

Similar to call-with-current-continuation, but applying the resulting continuation procedure does not remove any portion of the current continuation. Instead, application always extends the current continuation with the captured continuation (without installing any prompts other than those captured in the continuation).

When call-with-composable-continuation is called, if a continuation barrier appears in the continuation before the closest prompt tagged by prompt-tag, the exn:fail:contract:continuation exception is raised (because attempting to apply the continuation would always fail).

> procedure (call-with-escape-continuation proc) → any
> proc : (continuation? . -> . any)

Like call-with-current-continuation, but proc is not called in tail position, and the continuation procedure supplied to proc can only be called during the dynamic extent of the call-with-escape-continuation call.

A continuation obtained from call-with-escape-continuation is actually a kind of prompt. Escape continuations are provided mainly for backwards compatibility, since they pre-date general prompts in Racket. In the BC implementation of Racket, call-with-escape-continuation is implemented more efficiently than call-with-current-continuation, so call-with-escape-continuation can sometimes replace call-with-current-continuation to improve performance in those older Racket variants.

> procedure (call/ec proc) → any
> proc : (continuation? . -> . any)

The call/ec binding is an alias for call-with-escape-continuation.

> procedure (call-in-continuation k proc) → any
> k : continuation?
> proc : (-> any)

Similar to applying the continuation k, but instead of delivering values to the continuation, proc is called with k as the continuation of the call (so the result of proc is returned to the continuation). If k is a composable continuation, the continuation of the call to proc is the current continuation extended with k.

Examples:

> > (+ 1
> (call/cc (lambda (k)
> (call-in-continuation k (lambda () 4)))))
> 5
> > (+ 1
> (call/cc (lambda (k)
> (let ([n 0])
> (dynamic-wind
> void
> (lambda ()
> ; n accessed after post thunk
> (call-in-continuation k (lambda () n)))
> (lambda ()
> (set! n 4)))))))
> 5
> > (+ 1
> (with-continuation-mark
> 'n 4
> (call/cc (lambda (k)
> (with-continuation-mark
> 'n 0
> (call-in-continuation
> k
> (lambda ()
> ; 'n mark accessed in continuation
> (continuation-mark-set-first #f 'n))))))))
> 5

Added in version 7.6.0.17 of package base.

> syntax (let/cc k body ...+)

Equivalent to (call/cc (lambda (k) body ...)).

> syntax (let/ec k body ...+)

Equivalent to (call/ec (lambda (k) body ...)).

> procedure (call-with-continuation-barrier thunk) → any
> thunk : (-> any)

Applies thunk with a continuation barrier between the application and the current continuation. The results of thunk are the results of the call-with-continuation-barrier call.

> > procedure
> > (continuation-prompt-available? |  | prompt-tag |  |  |  |
> > | [ | cont]) |  | → |  | any
> prompt-tag : continuation-prompt-tag?
> cont : continuation? = (call/cc values)

Returns #t if cont, which must be a continuation, includes a prompt tagged by prompt-tag, #f otherwise.

> procedure (continuation? v) → boolean?
> v : any/c

Return #t if v is a continuation as produced by call-with-current-continuation, call-with-composable-continuation, or call-with-escape-continuation, #f otherwise.

> procedure (continuation-prompt-tag? v) → boolean?
> v : any/c

Returns #t if v is a continuation prompt tag as produced by default-continuation-prompt-tag or make-continuation-prompt-tag.

> > procedure
> > (dynamic-wind |  | pre-thunk |  |  |  |
> > |  | value-thunk |  |  |  |
> > |  | post-thunk) |  | → |  | any
> pre-thunk : (-> any)
> value-thunk : (-> any)
> post-thunk : (-> any)

Applies its three thunk arguments in order. The value of a dynamic-wind expression is the value returned by value-thunk. The pre-thunk procedure is invoked before calling value-thunk and post-thunk is invoked after value-thunk returns. The special properties of dynamic-wind are manifest when control jumps into or out of the value-thunk application (either due to a prompt abort or a continuation invocation): every time control jumps into the value-thunk application, pre-thunk is invoked, and every time control jumps out of value-thunk, post-thunk is invoked. (No special handling is performed for jumps into or out of the pre-thunk and post-thunk applications.)

When dynamic-wind calls pre-thunk for normal evaluation of value-thunk, the continuation of the pre-thunk application calls value-thunk (with dynamic-wind’s special jump handling) and then post-thunk. Similarly, the continuation of the post-thunk application returns the value of the preceding value-thunk application to the continuation of the entire dynamic-wind application.

When pre-thunk is called due to a continuation jump, the continuation of the call to pre-thunk

- jumps to a more deeply nested pre-thunk, if any, or jumps to the destination continuation; then

- continues the same as the enclosing dynamic-wind call in the destination continuation (i.e., matching the continuation of the original dynamic-wind call up to the enclosing prompt that delimited capture).

Normally, the second part of this continuation is never reached, due to a jump in the first part. However, the second part is relevant because it enables jumps to escape continuations that are contained in the continuation of the dynamic-wind call within the destination continuation. Furthermore, it means that the continuation marks (see Continuation Marks) and parameterization (see Parameters) for pre-thunk correspond to those of the enclosing dynamic-wind call. The pre-thunk call, however, is parameterize-breaked to disable breaks (see also Breaks).

Similarly, when post-thunk is called due to a continuation jump, the continuation of calling post-thunk jumps to a less deeply nested post-thunk, if any, or jumps to a pre-thunk protecting the destination, if any, or jumps to the destination continuation, then continues the same as the enclosing dynamic-wind call within the originating continuation for the jump. As for pre-thunk, the continuation marks and parameterization of the dynamic-wind call are in place for post-thunk, except that the call is further parameterize-breaked to disable breaks.

In both cases, the destination for a jump is recomputed after each pre-thunk or post-thunk completes. When a prompt-delimited continuation (see Prompts, Delimited Continuations, and Barriers) is captured in a post-thunk, it might be delimited and instantiated in such a way that the destination of a jump turns out to be different when the continuation is applied than when the continuation was captured. There may even be no appropriate destination, if a relevant prompt or escape continuation is not in the continuation after the restore; in that case, the first step in a pre-thunk or post-thunk’s continuation can raise an exception.

Examples:

> > (let ([v (let/ec out
> (dynamic-wind
> (lambda () (display "in "))
> (lambda ()
> (display "pre ")
> (display (call/cc out))
> #f)
> (lambda () (display "out "))))])
> (when v (v "post ")))
> in pre out in post out
> > (let/ec k0
> (let/ec k1
> (dynamic-wind
> void
> (lambda () (k0 'cancel))
> (lambda () (k1 'cancel-canceled)))))
> 'cancel-canceled
> > (let* ([x (make-parameter 0)]
> [l null]
> [add (lambda (a b)
> (set! l (append l (list (cons a b)))))])
> (let ([k (parameterize ([x 5])
> (dynamic-wind
> (lambda () (add 1 (x)))
> (lambda () (parameterize ([x 6])
> (let ([k+e (let/cc k (cons k void))])
> (add 2 (x))
> ((cdr k+e))
> (car k+e))))
> (lambda () (add 3 (x)))))])
> (parameterize ([x 7])
> (let/cc esc
> (k (cons void esc)))))
> l)
> '((1 . 5) (2 . 6) (3 . 5) (1 . 5) (2 . 6) (3 . 5))

##### 10.4.1`` Additional Control Operators🔗ℹ

(require racket/control) | package: base

The bindings documented in this section are provided by the racket/control library, not racket/base or racket.

The racket/control library provides various control operators from the research literature on higher-order control operators, plus a few extra convenience forms. These control operators are implemented in terms of call-with-continuation-prompt, call-with-composable-continuation, etc., and they generally work sensibly together. Many are redundant; for example, reset and prompt are interchangeable.

> procedure (call/prompt proc [prompt-tag handler] arg ...) → any
> proc : procedure?
> | prompt-tag |  | : |  | continuation-prompt-tag?
> |  |  | = |  | (default-continuation-prompt-tag)
> handler : (or/c procedure? #f) = #f
> arg : any/c

The call/prompt binding is an alias for call-with-continuation-prompt.

> procedure (abort/cc prompt-tag v ...) → any
> prompt-tag : any/c
> v : any/c

The abort/cc binding is an alias for abort-current-continuation.

> procedure (call/comp proc [prompt-tag]) → any
> proc : (continuation? . -> . any)
> | prompt-tag |  | : |  | continuation-prompt-tag?
> |  |  | = |  | (default-continuation-prompt-tag)

The call/comp binding is an alias for call-with-composable-continuation.

> procedure (abort v ...) → any
> v : any/c

Returns the vs to a prompt using the default continuation prompt tag and the default abort handler.

That is, (abort v ...) is equivalent to

> (abort-current-continuation
> (default-continuation-prompt-tag)
> (lambda () (values v ...)))

Example:

> > (prompt
> (printf "start here\n")
> (printf "answer is ~a\n" (+ 2 (abort 3))))
> start here
> 3

> syntax (% expr)
> (% expr handler-expr)
> (% expr handler-expr #:tag tag-expr)
> procedure (fcontrol v #:tag prompt-tag) → any
> v : any/c
> prompt-tag : (default-continuation-prompt-tag)

Sitaram’s operators [Sitaram93].

The essential reduction rules are:

> (% val proc) => val
> (% E[(fcontrol val)] proc) => (proc val (lambda (x) E[x]))
> ; where E has no %

When handler-expr is omitted, % is the same as prompt. If prompt-tag is provided, % uses specific prompt tags like prompt-at.

Examples:

> > (% (+ 2 (fcontrol 5))
> (lambda (v k)
> (k v)))
> 7
> > (% (+ 2 (fcontrol 5))
> (lambda (v k)
> v))
> 5

> syntax (prompt expr ...+)
> syntax (control id expr ...+)

Among the earliest operators for higher-order control [Felleisen88a, Felleisen88, Sitaram90].

The essential reduction rules are:

> (prompt val) => val
> (prompt E[(control k expr)]) => (prompt ((lambda (k) expr)
> (lambda (v) E[v])))
> ; where E has no prompt

Examples:

> > (prompt
> (+ 2 (control k (k 5))))
> 7
> > (prompt
> (+ 2 (control k 5)))
> 5
> > (prompt
> (+ 2 (control k (+ 1 (control k1 (k1 6))))))
> 7
> > (prompt
> (+ 2 (control k (+ 1 (control k1 (k 6))))))
> 8
> > (prompt
> (+ 2 (control k (control k1 (control k2 (k2 6))))))
> 6

> syntax (prompt-at prompt-tag-expr expr ...+)
> syntax (control-at prompt-tag-expr id expr ...+)

Like prompt and control, but using specific prompt tags:

> (prompt-at tag val) => val
> (prompt-at tag E[(control-at tag k expr)]) => (prompt-at tag
> ((lambda (k) expr)
> (lambda (v) E[v])))
> ; where E has no prompt-at for tag

> syntax (reset expr ...+)
> syntax (shift id expr ...+)

Danvy and Filinski’s operators [Danvy90].

The essential reduction rules are:

> (reset val) => val
> (reset E[(shift k expr)]) => (reset ((lambda (k) expr)
> (lambda (v) (reset E[v]))))
> ; where E has no reset

The reset and prompt forms are interchangeable.

> syntax (reset-at prompt-tag-expr expr ...+)
> syntax (shift-at prompt-tag-expr identifier expr ...+)

Like reset and shift, but using the specified prompt tags.

> syntax (prompt0 expr ...+)
> syntax (reset0 expr ...+)
> syntax (control0 id expr ...+)
> syntax (shift0 id expr ...+)

Generalizations of prompt, etc. [Shan04].

The essential reduction rules are:

> (prompt0 val) => val
> (prompt0 E[(control0 k expr)]) => ((lambda (k) expr)
> (lambda (v) E[v]))
> (reset0 val) => val
> (reset0 E[(shift0 k expr)]) => ((lambda (k) expr)
> (lambda (v) (reset0 E[v])))

The reset0 and prompt0 forms are interchangeable. Furthermore, the following reductions apply:

> (prompt E[(control0 k expr)]) => (prompt ((lambda (k) expr)
> (lambda (v) E[v])))
> (reset E[(shift0 k expr)]) => (reset ((lambda (k) expr)
> (lambda (v) (reset0 E[v]))))
> (prompt0 E[(control k expr)]) => (prompt0 ((lambda (k) expr)
> (lambda (v) E[v])))
> (reset0 E[(shift k expr)]) => (reset0 ((lambda (k) expr)
> (lambda (v) (reset E[v]))))

That is, both the prompt/reset and control/shift sites must agree for 0-like behavior, otherwise the non-0 behavior applies.

> syntax (prompt0-at prompt-tag-expr expr ...+)
> syntax (reset0-at prompt-tag-expr expr ...+)
> syntax (control0-at prompt-tag-expr id expr ...+)
> syntax (shift0-at prompt-tag-expr id expr ...+)

Variants of prompt0, etc., that accept a prompt tag.

> procedure (spawn proc) → any
> proc : ((any/c . -> . any) . -> . any)

The operators of Hieb and Dybvig [Hieb90].

The essential reduction rules are:

> (spawn proc)
> => (prompt/spawn tag
> (proc (lambda (proc) (abort/spawn tag proc))))
> ; where tag is a freshly generated prompt tag
> (prompt/spawn tag val)
> => val
> (prompt/spawn tag E[(abort/spawn tag proc)])
> => (proc (lambda (x) (prompt/spawn tag E[x])))
> ; where E has no prompt/spawn for tag

> procedure (splitter proc) → any
> | proc |  | : |  |
> (((-> any) . -> . any)
> ((continuation? . -> . any) . -> . any)
> . -> . any)

The operator of Queinnec and Serpette [Queinnec91].

The essential reduction rules are:

> (splitter proc)
> => (prompt/splitter tag
> (proc (lambda (thunk) (abort/splitter tag thunk))
> (lambda (proc) (control0/splitter tag k (proc k)))))
> ; where tag is a freshly generated prompt tag
> (prompt/splitter tag val)
> => val
> (prompt/splitter tag E[(abort/splitter tag thunk)])
> => (thunk)
> ; where E has no prompt/splitter for tag
> (prompt/splitter tag E[(control0/splitter tag k expr)])
> => ((lambda (k) expr)
> (lambda (x) E[x]))
> ; where E has no prompt/splitter for tag

> procedure (new-prompt) → continuation-prompt-tag?
> (new-prompt name) → continuation-prompt-tag?
> name : symbol?
> syntax (set prompt-expr expr ...+)
> syntax (cupto prompt-expr id expr ...+)

The operators of Gunter et al. [Gunter95].

In this library, new-prompt is an alias for make-continuation-prompt-tag, set is an alias for prompt0-at, and cupto is an alias for control0-at.

Changed in version 8.11.0.3 of package base: The new-prompt function is now really an alias for make-continuation-prompt-tag.

top contents ← prev up next →