I am looking at the following example for a coroutine from http://community.schemewiki.org/?call-with-current-continuation:
(define (hefty-computation do-other-stuff)
(let loop ((n 5))
(display "Hefty computation: ")
(display n)
(newline)
(set! do-other-stuff (call/cc do-other-stuff)) ; point A
(display "Hefty computation (b)")
(newline)
(set! do-other-stuff (call/cc do-other-stuff))
(display "Hefty computation (c)")
(newline)
(set! do-other-stuff (call/cc do-other-stuff))
(if (> n 0)
(loop (- n 1)))))
superfluous work:
;; notionally displays a clock
(define (superfluous-computation do-other-stuff)
(let loop ()
(for-each (lambda (graphic)
(display graphic)
(newline)
(set! do-other-stuff (call/cc do-other-stuff)))
'("Straight up." "Quarter after." "Half past." "Quarter til.")) ; point B
(loop)))
(hefty-computation superfluous-computation)
For the first usage of call/cc, what is the context supposed to be? When I say context, I mean where are we supposed to “return to” as a result of callcc’s jump?
From what I understand, the first time you call call/cc, do-other-stuff essentially becomes a procedure that executes the code of superfluous-computation and then jumps to the point right after the set! (point A). The second time, it will wrap its “jump to point B” behavior around the “jump to point A and execute the context, or whatever code follows point A”. Is this correct?
It doesn’t seem like this code would work if the set! actually happened. Or is the set! necessary for this code to work?
A visual representation of what’s going on would really help.
The context of
call/ccis where evercall/ccis being called from. You can almost think of acall/cclikegotothat jumps the code right back to where you were before and substitutes(call/cc whatever)with the return value.call/ccbasically says, "let’s go do this function and give it away to jump right back here and forget about whatever else it was doing"Ok when I was trying to understand
call/ccfor the first time, I found this code confusing in the extreme so let’s look at a simplified coroutine example:Ok this is exactly the same concept as your code. But it’s much simpler.
In this case, if we call
(r1 r2)this printsWhy? Because
r1first takes inr2ascont2so it announces to us that it’s inr1. And then it recurses on itself with the result of(call/cc cont2)aka(call/cc r2).Ok so what’s the return of this? well
(call/cc r2)callsr2with the current continuation ascont1so will announce that it’s inr2and then recurse on itself with the result of(call/cc cont1). Ok so what wascont1again?cont1was a continuation to that expression before inr1. So when we call it here, we pass back a continuation to the spot we’re currently at. Then we forget anything about what we were doing inr2and hop back into executingr1.This repeats in
r1now. We announce stuff and then jump back to where we where before inr2with and our expression from before,(call/cc cont1)returns a continuation to where we were inr1and then we continue in our merry infinite loop.Back to your code
In your code the concept is exactly the same. In fact `superfluous-computation` is almost identical to the above functions when you stop and think about it. So what’s up with the `set!`s? In this code all they do is change the value of `do-other-work` to the newest continuation. That’s it. In my example I used recursion. In this example they use `set!`.