[plt-scheme] Confusing continuation behavior (a question about)

From: Anton van Straaten (anton at appsolutions.com)
Date: Fri Jul 18 00:24:37 EDT 2008

Grant Rettke wrote:
> On Thu, Jul 17, 2008 at 6:36 PM, Anton van Straaten
> <anton at appsolutions.com> wrote:
>>> 1 -> 2 -> 3 -> 1 -> 2 -> 3 -> ...
>>> 1. (a-process)
>>> 2. (set! cond1 #t)
>>> 3. (a-process)
>>> 4. (set! cond2 #t)
>> Here's a clue: after line 3 invokes the continuation that re-enters 'impl',
>> what is the value of 'return'?
> Here are my thoughts. I am still studying continuations. Honestly, I
> have a hard time visualizing them

I don't think you should expect otherwise, they're pretty tricky.

> but from stepping through with the
> debugger I think I've got a visual of what is happening along with my
> rudimentary practice of converting simple functions to CPS style. Are
> these thoughts correct?
> The first time 'a-process' gets called at line 1, the the value bound
> to 'return' is a continuation that looks like this:
> (lambda (ignored)
>   (begin
>     (a-process)
>     (set! cond1 #t)
>     (a-process)
>     rest of program...))

Close.  The first call to a-process doesn't belong there, because you're 
trying to represent the continuation of that call, which doesn't include 
the call itself.  Just removing that first call gives a reasonable 

  (lambda (ignored)
    (set! cond1 #t)
    ...rest of program...)

This is correct as long as we ignore the interaction with the REPL, 
which is appropriate if the code is running as a module.

(In the original example, the return value of a-process gets displayed 
by the REPL, i.e. "Condition 1 not met" in this case.  But modeling that 
here would complicate things unnecessarily.)

It might help to think about this if the top-level program was a more 
functional expression, like this:

   (let ((result1 (a-process)))
     (set! cond1 #t)
     (let ((result2 (a-process)))
       ...rest of program...))

Explicitly saving the results of the call to a-process makes it easier 
to see the relationship to the continuation of the first call to 
a-process, which now looks more like this:

   (lambda (result1)
     (set! cond1 #t)
     (let ((result2 (a-process)))
       ...rest of program...))

...which of course is very similar to my first version above.

> The second time 'a-process' gets called at line 3, the value of
> 'return' is still the same as above because when impl was rebound,
> that was the value bound to return. 


> After the call at line 3, we
> really want return to be bound to this:
> (lambda (ignored)
>   (begin
>     ;;;;; (a-process)
>     ;;;;; (set! cond1 #t)
>     (a-process)
>     rest of program...))

Correct, except that again, the second call to a-process doesn't belong 
inside its own continuation.

> So the question is, when we pass control to impl that second time
> after binding cond1 to #t, how do we access the *that* continuation
> and bind it to return. I'm going to do some reading.

Here's a hint: on the second call, impl is still being invoked by 
call/cc, which passes a continuation to it.  There are a few important 
questions here (in case I've over-hinted, you might want to think about 
them one at a time instead of reading ahead):

1. What is that continuation?  You probably don't need to write it out 
exactly unless you want to, just figure out roughly where it's going to 
"return" to.

2. Is it useful?

3. What happens to it, i.e. where does the value go after it's passed to 
the continuation currently bound to impl?

Note that as with the top-level expressions, it might help to rely less 
on sequences of expressions, since they can make things harder to think 
about because their results are implicitly discarded.  This makes for 
sort of invisible gaps in the functional control flow, from our 
perspective of modeling the program's behavior.  An example of such an 
expression is:

   (let/cc resume-here (set! impl resume-here))

This leads to more questions:

4. What is being implicitly discarded by this expression, i.e. what is 
the result of the expression?

5. Can you tie all this together to fix the bug?  (Note that major 
rearrangement of the program isn't needed.)


Posted on the users mailing list.