Use call/cc to leave nested calls and return a result to an enclosing call. Use dynamic-wind to run cleanup when that exit occurs.
The exit point that call/cc passes to your procedure is called a continuation.
Run each complete example in the Playground. For an ordinary choice, use if or cond. For a helper that finishes normally, return its result.
Exit before the next expression
(call/cc (lambda (escape) (escape 'done) 'unreachable))Result
doneFollow the call in three steps.
call/cccalls thelambdawith an escape procedure. The parameterescapenames that procedure.(escape 'done)stops the lambda's remaining work and makes the enclosingcall/ccreturndone.- The expression
'unreachableis skipped. Evaluation continues aftercall/cc.
A continuation is the work remaining after a computation finishes. Here, escape represents the continuation of the enclosing call/cc. Calling it leaves the calls inside that call/cc. This is an upward escape.
Lispex allows this escape once, while its enclosing call/cc is still active. The escape procedure becomes used before cleanup starts. A later call to it reports E340, even if cleanup is still running.
Run entry, work, and cleanup
(dynamic-wind
(lambda () (display "before "))
(lambda () (display "during "))
(lambda () (display "after")))Output
before during afterdynamic-wind takes three procedures with no arguments.
| Procedure | When it runs |
|---|---|
before | On entry |
thunk | After before, to perform the main work |
after | After the main work, including an error or escape out of it |
A thunk is a procedure called with no arguments. On normal completion, dynamic-wind returns the main procedure's values. It discards the values returned by before and after.
Follow cleanup during an escape
(write
(call/cc
(lambda (escape)
(dynamic-wind
(lambda () (display "before "))
(lambda ()
(display "during ")
(escape 'done)
(display "unreachable "))
(lambda () (display "after "))))))
(newline)Output
before during after doneThe output shows the order.
beforeprintsbefore.- The main procedure prints
duringand callsescape. - Its remaining
displayis skipped. Theafterprocedure printsafter. call/ccreturnsdone. The surroundingwriteprints it, andnewlineends the line.
Put cleanup in after, where it also runs on an error or escape from the main procedure. If several dynamic-wind calls are exited, their cleanup procedures run from innermost to outermost.
If cleanup raises a new error or calls another active escape, the new error or escape replaces the one being handled. Calling the already-used escape again from cleanup instead produces E340.
Use an escape only while its call is active
(let ((saved #f))
(call/cc (lambda (escape) (set! saved escape) 'ready))
(saved 'late))Diagnostic
E340 — escape continuation is no longer activeHere, call/cc returns ready normally. That non-final result is discarded. Calling saved then fails because its enclosing call/cc has finished, even though the escape has never been used.
The same rule prevents re-entering a completed computation. before runs on entry and is not run again by an escape. To repeat work, use a loop or another ordinary procedure call.
Lispex passes escape and error signals through its evaluator to the enclosing call. These signals are separate from exceptions and continuations in the calling application's language.
See conditionals for local choices and diagnostics for reading errors.