One-Shot Continuations and dynamic-wind

The current `call/cc` is an upward, escape-only, non-reentrant one-shot continuation implemented through explicit evaluator signals.

When this matters

Use ordinary if, cond, and procedures first. Reach for call/cc only when a deeply nested computation needs one explicit upward escape, and add dynamic-wind when entry and cleanup must bracket that escape.

See it run

LISPEX
(call/cc (lambda (escape) (escape 'done) 'unreachable))

Observed result

OUTPUT
done

Read the example

call/cc passes an escape procedure to the lambda. Calling it with done immediately transfers that value to the enclosing call/cc; the later symbol unreachable is never evaluated. The escape is consumed by this transfer.

How to reason about it

  • The first invocation atomically consumes the continuation before transfer; every later invocation is E340.
  • dynamic-wind runs before, then thunk, then after; unwind runs pending after thunks innermost first.
  • A new error or escape from an after thunk replaces the signal already in flight.

Choose quickly

NeedPreferWhy
ordinary choiceif or condlocal control stays visible
return from a helpernormal procedure resultno non-local transfer
leave several nested calls oncecall/cc escapeone explicit upward exit
cleanup around a possible escapedynamic-windafter runs during unwind
resume the same point repeatedlyredesign the flowcontinuations are one-shot and non-reentrant

A common mistake

Multi-shot continuation reuse and dynamic-context re-entry are deferred.

Current boundaries

  • Host exceptions and host continuations do not implement guest transfer.

Keep going

Conditionals covers ordinary control flow. Diagnostics explains how errors interact with the explicit evaluator signal path.

Conditionals · Diagnostics