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/cc
is where ever call/cc
is being called from. You can almost think of a call/cc
like goto
that jumps the code right back to where you were before and substitutes (call/cc whatever)
with the return value.
call/cc
basically 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/cc
for the first time, I found this code confusing in the extreme so let's look at a simplified coroutine example:
(define r1
(lambda (cont)
(display "I'm in r1!")
(newline)
(r1 (call/cc cont))))
(define r2
(lambda (cont2)
(display "I'm in r2!")
(newline)
(r2 (call/cc cont2))))
Ok this is exactly the same concept as your code. But it's much simpler.
In this case, if we call (r1 r2)
this prints
I'm in r1
I'm in r2
I'm in r1
I'm in r2
I'm in r1
I'm in r2
I'm in r1
I'm in r2
...
Why? Because r1
first takes in r2 as cont
so it announces to us that it's in r1. And then it recurses on itself with the result of (call/cc cont)
aka (call/cc r2)
.
Ok so what's the return of this? well (call/cc r2)
will start executing r2 and announce that it's in r2 and then recurse on itself with the result of (call/cc cont2)
. Ok so what was cont2
again? cont2
was a continuation to that expression before in r1
. 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 in r2 and hop back into executing r1.
This repeats in r1
now. We announce stuff and then jump back to where we where before in r2 with and our expression from before, (call/cc cont2)
returns a continuation to where we were in r1
and then we continue in our merry infinite loop.
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!
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With