call-with-current-continuation, call/cc - capture the current continuation

LIBRARY

(import (rnrs))                     ;R6RS
(import (rnrs base))                ;R6RS
(import (scheme r5rs))              ;R7RS
(import (scheme base))              ;R7RS

SYNOPSIS

(call-with-current-continuation proc)
(call/cc proc)

DESCRIPTION

Packages the current continuation as an "escape procedure" and passes it as an argument to proc.

The escape procedure is a procedure that, if it is later called, will abandon whatever continuation is in effect at that later time and will instead reinstate the continuation that was in effect when the escape procedure was created.

Unlimited lifetime of the continuation
The escape procedure that is passed to proc has unlimited extent just like any other procedure in Scheme. It may be stored in variables or data structures and may be called as many times as desired.

Escape procedures in many other languages can only be called once. It is also a common limitation that escape procedures can only be called "upwards", meaning that it's impossible to re-enter a context once it has been returned from. This allows you to implement simpler things like exception handling, but it is very limited compared to what call/cc provides.

Tail call optimization
If a call to call/cc occurs in a tail context, the call to proc is also in a tail context.
Arity of the escape procedure
The escape procedure accepts the same number of arguments as the continuation of the original call to call/cc.

Simply speaking, this is any number of arguments if the call happened in a begin(7scm) and a single argument if it happened in the evaluation of an argument to a procedure call. If the continuation was created by call-with-values(3scm) then it accepts the same number of arguments as the consumer procedure.

Interaction with dynamic-wind
Calling the escape procedure may cause the invocation of before and after procedures installed using dynamic-wind(3scm). Another way of saying this is that calling an escape procedure reenters the dynamic extent of the call to call/cc, and thus restores its dynamic environment. This is relevant for things like parameters, guard(7scm), with-exception-handler(3scm), and with-input-from-file(3scm).

IMPLEMENTATION NOTES

Most common implementation strategies for Scheme use a stack for function calls. You may think of call/cc as a procedure that takes a copy of that stack and wraps it inside a procedure object. That procedure object, when called, overwrites the current stack with the contents of the captured stack. Some extra logic is also needed for running the dynamic-wind handlers.

Note however that this explanation is overly concrete and if that was the specification then many optimizations performed by compilers would be impossible. Because call/cc is specified on a language-level, not a concrete machine level, many optimizations can be performed in intermediate steps of the compiler. As an example, obvious uses of call/cc to return from a function can be optimized into function returns. No copying of the stack is needed for these simple case.

A full discussion on the various methods for implementing continuations is out of scope for this manual page, but here are some pointers for the interested reader: continuation-passing style, Cheney on the M.T.A. (CHICKEN) and segmented stacks (Chez Scheme).

RETURN VALUES

There are two different ways for this procedure to return. It can return via a call to the escape procedure and will in that case return whatever arguments were passed to that procedure. Alternatively, it can return by proc returning normally and in that case returns whatever values proc returned.

EXAMPLES

The following examples show only some ways in which call/cc is used. If all real uses were as simple as these examples, there would be no need for a procedure with the power of call/cc.

(call/cc
 (lambda (return)
   (return 47)
   (display "This does not run0)))
        => 47

(call-with-current-continuation
  (lambda (exit)
    (for-each (lambda (x)
                (when (negative? x)
                  (exit x)))
              '(54 0 37 -3 245 19))
    #t))
        => -3

(define list-length
  (lambda (obj)
    (call-with-current-continuation
      (lambda (return)
         (letrec ((r
                   (lambda (obj)
                     (cond ((null? obj) 0)
                           ((pair? obj)
                            (+ (r (cdr obj)) 1))
                           (else (return #f))))))
           (r obj))))))

(list-length '(1 2 3 4))  =>  4
(list-length '(a b . c))  =>  #f

(call-with-current-continuation procedure?)
        => #t

APPLICATION USAGE

Direct usages, as for returning from a procedure, are unusual in practice. More commonly the usages are hidden behind abstractions that implement new control mechanisms. Concurrent ML, coroutines and other forms of concurrency, exception handling, indeterminism and other control constructs can be implemented by the Scheme developer in user code.

RATIONALE

The following is quoted verbatim from R2RS (AI Memo No. 848, 1985).

The classic use of call-with-current-continuation is for structured, non-local exits from loops or procedure bodies, but in fact call-with-current-continuation is extremely useful for implementing a wide variety of advanced control structures.

Whenever a Scheme expression is evaluated there is a continuation wanting the result of the expression. The continuation represents an entire (default) future for the computation. If the expression is evaluated at top level, for example, then the continuation will take the result, print it on the screen, prompt for the next input, evaluate it, and so on forever. Most of the time the continuation includes actions specified by user code, as in a continuation that will take the result, multiply it by the value stored in a local variable, add seven, and give the answer to the top level continuation to be printed. Normally these ubiquitous continuations are hidden behind the scenes and programmers don’t think much about them. On rare occasions, however, when programmers need to do something fancy, then they may need to deal with continuations explicitly, call-with-current-continuation allows Scheme programmers to do that by creating a procedure that acts just like the current continuation.

COMPATIBILITY

This procedure is the same in all versions of Scheme since R2RS. It is usually missing in other languages.

ERRORS

This procedure can raise exceptions with the following condition types:
&assertion (R6RS)
The wrong number of arguments was passed or an argument was outside its domain. In particular, proc must be a procedure hat accepts one argument.
R7RS
The assertions described above are errors. Implementations may signal an error, extend the procedure's domain of definition to include such arguments, or fail catastrophically.

SEE ALSO

dynamic-wind(3scm), getcontext(3), longjmp(3)

STANDARDS

R4RS, IEEE Scheme, R5RS, R6RS, R7RS

HISTORY

The following is quoted verbatim from R2RS (AI Memo No. 848, 1985).

Most serious programming languages incorporate one or more special purpose escape constructs with names like exit, return, or even goto. In 1965, however, Peter Landin invented a general purpose escape operator called the J-operator. John Reynolds described a simpler but equally powerful construct in 1972. The catch special form described by Sussman and Steele in the 1975 report on Scheme is exactly the same as Reynolds's construct, though its name came from a less general construct in MacLisp. The fact that the full power of Scheme's catch could be obtained using a procedure rather than a special form was noticed in 1982 by the implementors of Scheme 311, and the name call-with-current-continuation was coined later that year. Although the name is descriptive, some people feel it is too long and have taken to calling the procedure call/cc.

AUTHORS

This page is part of the scheme-manpages project. It includes materials from the RnRS documents. More information can be found at https://github.com/schemedoc/manpages/.

BUGS

Using call/cc in deeply nested recursions can cause performance problems in simple implementations. An example of a bad case is when map(3scm) is implemented using recursion and the procedure passed to it uses guard(7scm). Because guard(7scm) uses call/cc internally, each processed element of the list will grow the stack. The call to map(3scm) that was expected to take linear time will then instead take quadratic time. Therefore implementations that have a simple call/cc implementation should avoid having a recursive definition of map(3scm).


Markup created by unroff 1.0sc,    March 04, 2023.