[Python-Dev] uthread strawman

Christian Tismer tismer@tismer.com
Thu, 09 Nov 2000 13:39:48 +0200

Tim Peters wrote:

> Building a backtracking parser directly out of continuations sounds to me
> mostly painful.  Building generators out of continuations *is* painful (I've
> done it).  Curiously, the symmetry of coroutines appears to make building
> them out of continuations easier (than building generators).

Some things work very well, built with plain continuations.
See the attached ICON-style generator/backtrack framework
(going to post the 8 queens puzzle, soon).

> I'm not in love w/ continuations:  I *could* be if Guido got Continuation
> Religion and wanted to redo exceptions and calls on top of continuations
> too, but given that whatever happens here is destined (Christian will say
> "doomed" <wink>) to co-exist with everything that's already here, the appeal
> of continuations is minimal.  I've got no desire to play with novel new
> control structures in Python (note that I don't consider generators-- or
> even coroutines --to be "novel", not after they've been in multiple
> languages for more than 30 years), and Python doesn't have the syntactic
> flexibility that makes such experiments *pleasant* in Scheme anyway.

That's a very good point. Tricking Python to make continuations
useful is a pain in the a** and has led me to a quite weird API.
After the "sane" constructs are defined well, there is no much
reason to support continuations in the first place.

> So it's enough for me if Python supports the handful of new control-flow
> gimmicks (generators, coroutines, maybe uthreads) tasteful *users* ask for;
> if we don't need continuations for those, fine by me.  BTW, I'd also like to
> pickle a pure-Python computation in mid-stream, save it to disk, and resume
> it later after a reboot (or on another machine!); we don't need
> continuations for that either (although we do need much of what Stackless
> does).

There is one application of continuations which I still consider
worthy. I'm shure that many people find it incredibly ugly.
Using continuations, I can build method-like functions
without classes and instances, which perform incredibly
fast. This cannot be done with simple one-shot continuations;
of course a class method would do the same, but slower:

<script labguage="python">
function expensive_to_init_and_cheap_to_call(*args):
    pass # initialize many local variables
    param = continuation.return_current() # ***
    return do_cheap_calculation(param)

# generate and initialize such a function    

fastfunc = expensive_to_init_and_cheap_to_call(manyargs)

# now we are at ***

fastfunc(42)  # can call it many times, at high speed.

This is a function with expensive initialization and many
local variables involved. After initializing, the
continuation of *** is returned as a callable object.
All initialization is done, all locals are set, and
now we can pull out many results by repeatedly calling
this continuation.
This cannot be modelled as efficiently today with classes.

ciao - chris

p.s.: Here the simple ICON-like generator/backtrack framework.


import continuation

class control:
    """ ICON style generators """
    def __init__(self):
        # the chain of alternatives is a stack of tuples
        self.more = None

    def suspend(self, value):
        """ return a value, but keep the caller for re-use """
        # push the caller on the alternatives stack
        self.more = (continuation.caller(), self.more)
        # simulate a return of the caller with the current value

    def fail(self):
        """ restart an alternative, if there is some.
        Otherwise do nothing """
        if self.more:
            back, self.more = self.more

    def clear(self):
        """ clear alternatives stack """
        self.more = None

    def asn(self, old, val):
        """ an undoable assignment. Usage:
        var = c.asn(var, value)
        Like the ICON operator "<-"
        print "asn returning"

    def choice(self, *args):
        """ iterator over a fixed sequence of values
        Like the ICON operator "|"
        if len(args) == 1:
            args = args[0]
        for val in args[:-1]:
        return args[-1]

    # the above works only for sequences of known size.
    # The version below is better since it does not need to
    # know the size, but it has to do a look-ahead.
    def choice(self, *args):
        """ iterator over a general sequence of values
        Like the ICON operator "|"
        if len(args) == 1:
            args = args[0]
        # problem: how do we *return* the last element for any sequence?
        # solution: do a look-ahead by 1
        first = 1
        for nextval in args:
            if first:
                val = nextval
                first = 0
            val = nextval
        return val

    def to_by(self, a, b=None, c=None):
        """ a loop construct, modelled after the ICON
        for .. to .. by expression, but using xrange style
        if c is None:
            if b is None:
                iterator = xrange(a)
                iterator = xrange(a,b)
            iterator = xrange(a,b,c)
        # problem: how do we *return* the last element?
        # solution: splitting the xrange is handy!
        for i in iterator[:-1]:
        return iterator[-1]

    # trying to get *slightly* more structure, here an
    # attempt to introduce something like "every".


Christian Tismer             :^)   <mailto:tismer@tismer.com>
Mission Impossible 5oftware  :     Have a break! Take a ride on Python's
Kaunstr. 26                  :    *Starship* http://starship.python.net
14163 Berlin                 :     PGP key -> http://wwwkeys.pgp.net
PGP Fingerprint       E182 71C7 1A9D 66E9 9D15  D3CC D4D7 93E2 1FAE F6DF
     where do you want to jump today?   http://www.stackless.com