Re: coroutines and continuations ( in Python? - long discussion )

Steven D. Majewski (sdm7g@elvis.med.virginia.edu)
Sun, 1 May 1994 14:54:06 -0400

Since I'm at home on the modem, detailed response to your example
and some of your points will have to wait until I get a hardcopy
to study, but I'll comment on a few points now:

Re: restartable exceptions:
[ Tim on why it's not something he wants or desires... ]

sdm> - but the way around this is to NOT change the semantics of exceptions,
sdm> but merely to allow a 'raise' to pass a continuation object as its
sdm> optional arg.

tim> Agreed that if it must be done <wink>, that's the way to do it.

Restartable state after an exception was NOT one of my goals, either.
I was just commenting on the fact that it would become possible.

sdm> ...
sdm> One way is to make continuation objects callable, just like functions,
sdm> methods, and class creators:
sdm>
sdm> result, cont = cofunc( args ) # returns a value, continuation tuple
sdm> use( result )
sdm> result, cont = cont() # re-enter

tim> You don't want the 1st call to look different than all the others; any
tim> concrete, specific example would convince you of that instantly <smile>.

I think you are correct about that, but:

tim> I'd suggest this instead, where "resfunc" is a new built-in (whatever)
tim> with the same signature as the built-in "apply":

You can get around that by wrapping the first call:
cont = lambda: apply( cofunc, args )
...
while SOMETHING:
result, cont = cont()
use( result )

tim> This gives an interface that _just_ returns a result, each time
tim> resfuncobject is invoked. That's all I'm after. Of course there's
tim> nothing to stop callable_object from returning a (result, continuation)
tim> tuple if you prefer that instead. 99+% of the time the "continuation"
tim> I'm after will simply be the ability to resume the resfunc exactly where
tim> it left off, and the implementation sketch caters to that case by making
tim> it a no-work-on-the-user's-part case.

And if you don't want to explicitly handle the continuation, then that
can be wrapped up and hidden in a class.

I don't disagree with what you're after. I just think that my version
is closer to the implementation ( at least the one I have in mind ) and
thus the more primitive and builtin one. Since I don't see anything special
to be done on the initial call of the function - the difference
between a regular function and a resumable function is just that one
does a 'return' and the other does a suspend ( i.e. returns a
continuation ) I don't see the need for a special function to
create one.

But I'm all for adding things on top of that to make the typical
cases standard and easy syntax.

tim> Believe that life is more complicated with general co-routines:

Yes. ( more on that later... )

tim> I.e. for a resumed resfunc, return/suspend always returns to the point of
tim> resumption.

sdm> [ I'm not yet sure how or whether to fit args to the continuation into
sdm> this scheme.

tim> Think I'd rarely (never?) want that.

Agreed.

sdm> Or how exactly to express "capture the current continuation" in a
sdm> return statement.

tim> My hope is that this would be close to a nop. I.e., so long as the
tim> resfunc is alive, a reference to the frameobject is active in the
tim> _Resfunc instance, so the frameobject should persist by magic. In other
tim> words, "return" isn't enough to make the frameobject vanish. Everything
tim> needed should survive by magic, assuming-- as you said earlier --that
tim> frameobjects are extended to preserve enough interpreter state to
tim> _enable_ resumption. But then I'm starting to suspect I'm after
tim> something simpler than you're after ...

Yes - you're after something simpler.
Yes - what you said above should be true: as long as a reference to
the continuation object exists, it should be resumable, and a wrapper
class can keep that reference in an instance variable for you.
No - you didn't get what I meant by that sentence. i.e. I agree
but that's a different point.

sdm> ... vague mumbling about whether the thing that creates a
sdm> continuation ought to be a (pseudo)function or an expression in
sdm> Python goes here ... :-) ]

[ ... Tim on Icon's create() and his resfunc() ... ]

Again: what I was talking about was not creation of the resumable
function, but creation of a resumable frame by the 'suspend'
( or whatever syntax )

sdm> The type of "wrapper" I was envisioning in the pipe example would be a
sdm> class that takes two functions as args, and rebinds stdin/stdout to it
sdm> self. Its read and write methods encapsulate the coroutine control:
sdm> the called functions do no explicit save/restore of state - they
sdm> merely call obj.read or obj.write. ( Steve waves his hand magically,
sdm> here: ...

tim> + I do believe things are tougher if you want to spoof read/write: a
tim> resfunc decides when _it_ wants to yield control, but in spoofing
tim> read/write you want something else (namely the read/write proxies) to
tim> decide that its _invoking_ function wants to yield control. So that
tim> notion of resumability spills across frames.

Yes. Although a 'suspend value' would be a simple syntax for "return
a ( value, continuation ) tuple with the continuation causing
resumption of the function at the next statement", it doesn't give me
a way to express what I wanted to do in spoofing read/write.
( And when I figured that out last night, I figured out exactly what
you meant about resumable functions as a simpler subset of generalized
coroutines! ) What my problem requires is a swap of continuations:
read and write both save a current continuation and resume a previous
one. As you said - it's a more symetrical and complicated problem than
just a non-symetrical restart.

tim> Should have taken you at your word to begin with <wink>: you've convinced
tim> me we're really after different things here. I'm trying to find an
tim> easier way to write some kinds of programs to begin with, and you're
tim> trying to find a way to trick existing programs into doing things their
tim> authors didn't intend. How's that for a fair summary <grintim>?

I want both, but that's a fair summary, as until this problem, I was
convinced ( but, as you were, unsatisfied ;-) that Classes and some
non-trivial bookeeping could give the same results. ( And maybe I
had some hope that the problem could be generalized into a one-time
wrapper somehow - like I eventually did with my class that coerces
things into a sequence. )

sdm> ...
sdm> Well - I started out trying to solve the "pipe" problem, but then
sdm> discovered (suprisingly!) that the simplest solution is also the
sdm> most general one.
tim>
tim> For the kinds of problems I envision solving via the resfunc approach,
tim> messing with tuple returns and explicit continuations would be painful --
tim> although less painful than what I'm doing now. So I think of explicit
tim> continuations as being "simplest and most general" in much the way I
tim> think "go to" is the simplest and most general control structure <0.4
tim> grin>.

Yes. I said above: "closer to the implementation"

sdm> However - what I DON'T want is scheme-like continuation syntax.

tim> Have a brief sketch of that handy?

No. I have to reread the documentation whenever I want to use them.
Subtle and powerful.

tim> So whatever "generalized continuations" means exactly, if it subsumes
tim> general co-routines I bet I could learn to hate it <0.9 grintim>.

It subsumes general co-routines, non-local goto's and exception
handling and finally-type things. As one of the less
object-oriented of functional-programmers once put it:
"Why would you ever want objects when you have closures and
continuations."

tim> Well, that's what examples are for (else we're using words without shared
tim> referents). Writing up my examples in excruciating detail has really
tim> helped me understand what I would want here, and I'm surprised to find
tim> that "what I want" (resfuncs) really wouldn't give you a pleasant
tim> solution to your examples. But I'm not sure that what you want would
tim> give you a pleasant solution either! How about fleshing out your general
tim> redirection problem via coding a full solution "as if" Python already had
tim> what you want? I find a lot of things I _don't_ want that way <smiletim>.

I just wanted to make the point than although I'm starting from a
possible implementation ( i.e. it was seeing that implementing it
didn't look as difficult as I had initially thought ) I don't want
to bind the syntax to tightly to a particular implementation if I
can avoid it.

The problem I'm having in pseudo-coding my example is the problem
of expressing capturing a particular continuation without drawing
arrows and lines.

I think it's simple to say that:

suspend value
#### > continue HERE:

returns ( value, cont ) such that cont() resumes at the HERE label,
and the meaning doesn't get confused if I substitute any expression
of function(s) for 'value'.

What I need for my read/write problem is a "save this continuation
(insert arrow) HERE and resume that continuation THERE" syntax.

Modula has a syntax for coroutine call, though I can't recall it
offhand. ( Guido? ) I suspect that it does the sort of 'swap'
that I require, but hides the continuation as you would like.

Clearly, one of my problems here is limited exposure to
"general coroutines" , but experience with the more limited
for of Icon coexpressions, and the more general concepts of
continuation or threads. ( Not the same - I just mean that
in unix one gets used to using processes in cases where they
will often wait on each other, so they are functioning as
coroutines - just cause pipes are easy to express. )

So maybe I *will* have to give up the idea of passing, assigning,
handling, poking and otherwise prodding continuation explicitly.
However, I think this produces some OTHER problems which I won't
go into right now.

-- Steve Majewski (804-982-0831) <sdm7g@Virginia.EDU> --
-- UVA Department of Molecular Physiology and Biological Physics --
-- Box 449 Health Science Center Charlottesville,VA 22908 --
[ "Cognitive Science is where Philosophy goes when it dies ...
if it hasn't been good!" - Jerry Fodor ]