4 ms·
Steps to understanding call/cc (for C programmers): 0. recognize that function return in C (and many langs) is just like implicitly calling a functi
by cryptonector 8y ago
Steps to understanding call/cc (for C programmers):
0. recognize that function return in C (and many langs)
is just like implicitly calling a function of one
argument (the return value)
1. further recognize that these "return functions" are
*closures*! on the stack they are passed as both,
an instruction pointer *and* the frame pointer of the
frame into which to return!
2. add closures (somewhat like GCC local functions)
3. make a builtin macro-like thing that lets you call
a C function with an alternative "return-function"
argument that is like any other function (of one
argument), and particularly allows local function
references to be given as a return-function
4. make a builtin macro-like thing to get at the current
frame's return-function "argument" -- i.e., reify
the previously implicit current continuation
5. screw the function call stack, change the code
generator and run-time to allocate function frames
on a heap (and add garbage collection)
Presto, you have call/cc.
Shorter: allocate function frames on the stack, reify the return function argument closure and call it the continuation. To "reify" means to give reality to something that was previously hidden / implied.
call/cc looks really awesome until you realize that it has / causes problems. One problem is that if you hold on to a reference to a continuation then you're actually preventing GC of all the stack frames captured by that continuation. Then there are thread safety issues... Any language that has variables (as opposed to lexical, immutable bindings) will have thread safety problems. Imagine using closures as callbacks, and having said closures change variables they close over, and now imagine them racing against each other to modify the same variables. Well, in a Scheme continuations are strung-up closures, so now imagine multiple threads trying to call the same continuation. These thread-safety problems run deep, and will tend to push one towards Haskell or Rust.
Really, call/cc is a bit of a parlor trick :) -- a fascinating parlor trick, but still a parlor trick.
If you want much much more detail, I recommend two great books that deal with this subject:
- On Lisp (by Paul Graham)
- Lisp In Small Pieces
In On Lisp, Paul Graham has a chapter where he builds a set of macros that give you Scheme-style continuations with minimal compromises in how you write Lisp code.
Lisp In Small Pieces is all about writing Lisp/Scheme interpreters and compilers, so it touches on all of this depth and great detail.
- cryptonector 8y ago> Shorter: allocate function frames on the stack, reify the return function argument closure and call it the continuation. To "reify" means to give reality to something that was previously hidden / implied. Oops, s/stack/heap/.
- cryptonector 8y agoI should also add that though call/cc is a parlor trick, continuation passing style (CPS) is not. Making continuations explicit is a standard technique in compiler design, and it pays off handsomely. Also, while i'm at it, call/cc can be used to implement co-routines, which is fine enough, though coroutines can also just be provided by the language anyways. It is elegant that you can get co-routines as simple function calls rather than code that has to save/restore registers.