Question

This is related to What is call/cc?, but I didn't want to hijack this question for my own purposes, and some of its arguments like the analogy to setjmp/longjmp evade me.

I think I have a sufficient idea about what a continuation is, I think of it as a snapshot of the current call stack. I don't want to go into the discussion why this might be interesting or what you can do with continuations. My question is more specifically, why do I have to provide a function argument to call/cc? Why doesn't call/cc just return the current continuation, so I could do whatever I please with it (store it, call it, you name it)? In a link from this other question (http://community.schemewiki.org/?call-with-current-continuation-for-C-programmers), it talks about "Essentially it's just a clean way to get the continuation to you and keep out of the way of subsequent jumps back to the saved point.", but I'm not getting it. It seems unnecessarily complicated.

Was it helpful?

Solution 4

Against common SO netiquette I'm answering my own question, but more as the editor than the provider of the answer.

After a while I started a similar question over at LtU. After all, these are the guys that ponder language design all day long, aren't they, and one of the answers finally kicked in with me. Now things mentioned here, e.g. by Eli or in the original question, make much more sense to me. It's all about what gets included in the continuation, and where the applied continuation sets in.

One of the posters at LtU wrote:

"You can see exactly how call/cc allows you to "keep out of the way." With em or get/cc you need to do some kind of test to determine if you have a back-jump or just the initial call. Basically, call/cc keeps the use of the continuation out of the continuation, whereas with get/cc or em, the continuation contains its use and so (usually) you need to add a test to the beginning of the continuation (i.e. immediately following get/cc / em) to separate the "using the continuation parts" from the "rest of the continuation" parts."

That drove it home for me.

Thank you guys anyway!

OTHER TIPS

If you use a construct like Jay shows, then you can grab the continuation, but in a way, the value that is grabbed is already spoiled because you're already inside that continuation. In contrast, call/cc can be used to grab the continuation that is still pending outside of the current expression. For example, one of the simplest uses of continuations is to implement a kind of an abort:

(call/cc (lambda (abort)
           (+ 1 2 (abort 9))))

You cannot do that with the operation you describe. If you try it:

(define (get-cc) (call/cc values))
(let ([abort (get-cc)]) (+ 1 2 (abort 9)))

then you get an error about applying 9 as a procedure. This happens because abort jumps back to the let with the new value of 9 -- which means that you're now doing a second round of the same addition expression, except that now abort is bound to 9...

Two additional related notes:

  1. For a nice an practical introduction to continuations, see PLAI.
  2. call/cc is a little complex in that it takes in a function -- a conceptually easier to use construct is let/cc which you can find in some implementations like PLT Scheme. The above example becomes (let/cc abort (+ 1 2 (abort 9))).

That would be less versatile. If you want that behavior, you can just do:

(call/cc (lambda (x) x))

You could take a look at the example usages of continuations in "Darrell Ferguson and Dwight Deugo. "Call with Current Continuation Patterns". 8th Conference on Pattern Languages of Programs. September 2001." (http://library.readscheme.org/page6.html) and try to rewrite them using a call/cc-return, defined as above.

I suggest starting by asking yourself: what does it mean to be a first-class continuation?

The continuation of an expression essentially consists of two pieces of data: first, the closure (i.e., environment) of that expression; and second, a representation of what should be done with the result of the expression. A language with first-class continuations, then, is one which has data structures encapsulating these parts, and which treats these data structures just as it would any other.

call/cc is a particularly elegant way to realise this idea: the current continuation is packaged up as a procedure which encapsulates what-is-to-be-done-with-the-expression as what the procedure does when applied to the expression; to represent the continuation this way simply means that the closure of this procedure contains the environment at the site it was invoked.

You could imagine realising the idea of first-class continuations in other ways. They wouldn't be call/cc, and it's hard for me to imagine how such a representation could be simpler.

On a parting note, consider the implementation of let/cc that Eli mentioned, which I prefer to call bind/cc:

(define-syntax bind/cc
    (syntax-rules ()
        ((bind/cc var . body)
             (call/cc (lambda (var) . body)))))

And as an exercise, how would you implement call/cc based on bind/cc?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top