5 ms·
Like the blog author, I found the traditional explanation incredibly confusing: "A continuation is the remaining work to be done". I, too, found the "copies of
by jcl 19y ago
Like the blog author, I found the traditional explanation incredibly confusing: "A continuation is the remaining work to be done". I, too, found the "copies of the stack" explanation much better.
The first problem I had was understanding that the "work remaining to be done" was not measured from the point of the call/cc statement, but just after, so it was really "the work remaining to be done once you get done with this next bit here, assuming you do." It was much easier for me to think of it as an instruction pointer saved to the stack, which points to the instruction to be executed on return, not the current instruction.
Likewise, I was confused by the idea that you could save a continuation so that you could call it again and again. "Why," I thought, "would you want to execute a continuation more than once if it is just going to return the same thing each time?" It turns out I was thinking in purely functional terms, and Scheme is not a purely functional language; if you allow for side-effects, a continuation can return a different thing each time. The "work remaining to be done" explanation misses the fact that a continuation captures the state of the code and some data, but it doesn't capture all data. The stack explanation explains precisely what data is saved by the continuation and what data is subject to side-effects.
- jules 19y agoAha, but continuations are useful even without side effects. That's the beauty! (let ((p (call/cc (lambda (k) (cons 1 k))))) (print (car p)) (if (< (car p) 10) ((cdr p) (cons (+ 1 (car p)) (cdr p))) 'finished)) Can you understand what it does? No (set!) in sight! Bonus: (define (mark initial) (call/cc (lambda (c) (define (return value) (c (cons value return))) (cons initial return)))) (mark x) returns a pair of values. The car is x, the cdr is a (return) function. When you call (return y), the (mark x) call returns again, this time returning y instead of x. Here's an infinite loop example: (let ((pair (mark 1))) (let ((value (car pair)) (return (cdr pair))) ; unpack the pair (print value) (return (+ value 1)))) ; jump back to the (mark 1) call, this time returning value+1 Here's a Ruby implementation of mark: def mark(initial) callcc do |c| ret = lambda{|value| c.call([value,ret])} [initial, ret] end end (I used ret instead of return because return is a Ruby keyword) Example in Ruby: value, ret = mark 1 puts value ret.call(value + 1)