Warning: This is the manual of the legacy Guile 2.0 series. You may want to read the manual of the current stable series instead.

Next: , Previous: , Up: Control Mechanisms   [Contents][Index]

6.13.6 Continuations

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)

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)

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.

Scheme Procedure: call-with-current-continuation proc
Scheme Procedure: call/cc proc

Capture the current continuation and call (proc cont) with it. The return value is the value returned by proc, or when (cont value) is later invoked, the return is the value passed.

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.

The call in the name call-with-current-continuation refers 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/cc is an alias for call-with-current-continuation. This is in common use since the latter is rather long.

Here is a simple example,

(define kont #f)
(format #t "the return is ~a\n"
        (call/cc (lambda (k)
                   (set! kont k)
⇒ 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 format. The lambda stores this in 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. call/cc captures that like any other return, which is why interactively kont will come back to read more input.

C programmers may note that call/cc is like setjmp in the way it records at runtime a point in program execution. A call to a continuation is like a longjmp in 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/cc on resuming there. However longjmp can 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, continuations captured by call/cc 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 prompts (see Prompts). 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).

Next: , Previous: , Up: Control Mechanisms   [Contents][Index]