A “continuation” is the code that will execute when a given function or expression returns. For example, consider
(define (foo) (display "hello\n") (display (bar)) (newline) (exit))
The continuation from the call to
bar comprises a
display of the value returned, a
newline and an
exit. This can be expressed as a function of one argument.
(lambda (r) (display r) (newline) (exit))
In Scheme, continuations are represented as special procedures just like this. The special property is that when a continuation is called it abandons the current program location and jumps directly to that represented by the continuation.
A continuation is like a dynamic label, capturing at run-time a point in program execution, including all the nested calls that have lead to it (or rather the code that will execute when those calls return).
Continuations are created with the following functions.
Normally cont should be called with one argument, but when the location resumed is expecting multiple values (see Multiple Values) then they should be passed as multiple arguments, for instance
(cont x y z
cont may only be used from the same side of a continuation barrier as it was created (see Continuation Barriers), and in a multi-threaded program only from the thread in which it was created.
The call to proc is not part of the continuation captured, it runs only when the continuation is created. Often a program will want to store cont somewhere for later use; this can be done in proc.
callin the name
call-with-current-continuationrefers to the way a call to proc gives the newly created continuation. It's not related to the way a call is used later to invoke that continuation.
call/ccis an alias for
call-with-current-continuation. This is in common use since the latter is rather long.
Capture the current continuation as described above. The return value is the new continuation, and *first is set to 1.
When the continuation is invoked,
scm_make_continuationwill return again, this time returning the value (or set of multiple values) passed in that invocation, and with *first set to 0.
Here is a simple example,
(define kont #f) (format #t "the return is ~a\n" (call/cc (lambda (k) (set! kont k) 1))) ⇒ the return is 1 (kont 2) ⇒ the return is 2
call/cc captures a continuation in which the value returned is
going to be displayed by
lambda stores this
kont and gives an initial return
1 which is
displayed. The later invocation of
kont resumes the captured
point, but this time returning
2, which is displayed.
When Guile is run interactively, a call to
format like this has
an implicit return back to the read-eval-print loop.
captures that like any other return, which is why interactively
kont will come back to read more input.
C programmers may note that
setjmpin the way it records at runtime a point in program execution. A call to a continuation is like a
longjmpin that it abandons the present location and goes to the recorded one. Like
longjmp, the value passed to the continuation is the value returned by
call/ccon resuming there. However
longjmpcan only go up the program stack, but the continuation mechanism can go anywhere.
When a continuation is invoked,
call/cc and subsequent code
effectively “returns” a second time. It can be confusing to imagine
a function returning more times than it was called. It may help
instead to think of it being stealthily re-entered and then program
flow going on as normal.
dynamic-wind (see Dynamic Wind) can be used to ensure setup
and cleanup code is run when a program locus is resumed or abandoned
through the continuation mechanism.
Continuations are a powerful mechanism, and can be used to implement almost any sort of control structure, such as loops, coroutines, or exception handlers.
However the implementation of continuations in Guile is not as efficient as one might hope, because Guile is designed to cooperate with programs written in other languages, such as C, which do not know about continuations. Basically continuations are captured by a block copy of the stack, and resumed by copying back.
For this reason, generally continuations should be used only when there is no other simple way to achieve the desired result, or when the elegance of the continuation mechanism outweighs the need for performance.
Escapes upwards from loops or nested functions are generally best handled with exceptions (see Exceptions). Coroutines can be efficiently implemented with cooperating threads (a thread holds a full program stack but doesn't copy it around the way continuations do).