call-wit...inuation(3scm)
π¦ call-with-current-continuation, call/cc β Scheme Programmer's Manual, call-wit...inuation(3scm) π¦
π NAME π
call-with-current-continuation, call/cc β capture the current continuation, bestie. Thatβs the whole vibe. β¨
π LIBRARY π
okay so you can import it from these spots, no cap:
(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 πΈ
So call-with-current-continuation packages up the current continuation as an βescape procedureβ and passes it as an argument to proc. Iconic behavior, honestly. π
That escape procedure is a procedure that, if it gets called later, will abandon whatever continuation is in effect at that later time and instead reinstate the continuation that was in effect when the escape procedure was created. The way that works is genuinely main character energy. π«Ά
π Unlimited lifetime of the continuation
The escape procedure 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. No βone call onlyβ nonsense here. π
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. That allows you to implement simpler things like exception handling, but it is very limited compared to what call/cc provides. Like, sure, cute for a lil exception, but call/cc is the whole toolkit. π₯
π Tail call optimization
If a call to call/cc occurs in a tail context, the call to proc is also in a tail context. Tail-call besties stay winning. β¨
π 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. Very βit depends on the contextβ of it all. π¦
π 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). So yeah, the dynamic environment is coming with you. Periodt. π«
π οΈ 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. Thatβs the lowkey mental model, bestie. π§
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. Compiler girlies ate that one. π
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). iykyk. πβ¨
π 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. Two paths, both valid, both slay. π
π 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. Fr fr, these are just the appetizers. π
(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. So yeah, the real glow-up is when itβs under an abstraction. π
π RATIONALE π
okay so this next bit is literally quoted verbatim from R2RS (AI Memo No. 848, 1985), no edits, bestie:
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. Not everybody has this range, and thatβs their loss. π
π 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. (Yes, βhatβ is what the original says. We keep the details, always. π«‘)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. That last one is giving βwe ball, but with consequencesβ ππ
π SEE ALSO π
dynamic-wind(3scm)getcontext(3)longjmp(3)
Go read those too, bestie. π§
π STANDARDS π
R4RSIEEE SchemeR5RSR6RSR7RS
Certified standards icon. β
π 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/. Shoutout to the scheme-manpages girlies for preserving the lore. π
π 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). Thatβs the βuh oh, the vibe check failedβ part. π
okay bestie, thatβs the whole page! π this iconic manual page is dated 2023-02-16 and belongs to the call-wit...inuation(3scm) manual, from the scheme-manpages source/version universe. go forth and capture continuations responsibly, periodt. π«Άβ¨
π¦ where this page comes from
- nixpkgs unstable: scheme-manpages 0-unstable-2024-02-11
- π the page's license: MIT
- Copyright (c) 2023 Gwen Weinholt
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms