[Python-ideas] Cofunctions: It's alive! Its alive!
Guido van Rossum
guido at python.org
Tue Aug 10 17:57:10 CEST 2010
I'm convinced of the utility. I still find the mechanism somehow odd
or clumsy; the need for two new keywords (codef and cocall), a new
builtin (costart), and a new api (__cocall__) doesn't sit well.
Please don't consider this a -1; I think there's something that can be
done (either to my mind or to the proposal :-).
I suppose I should look again at goroutines and see what syntax and
other rules they use.
In the mean time let me ask a few more questions (sorry if these have
been answered before, my attention is focusing in and out of this
thread):
- Is it possible to mix and match yield, yield from, and cocall in the
same function? Should / shouldn't it be?
- Would it be sufficient if codef was a decorator instead of a
keyword? (This new keyword in particular chafes me, since we've been
so successful at overloading 'def' for so many meanings -- functions,
methods, class methods, static methods, properties...)
- If we had cocall, would yield from still be useful? (I suppose yield
from is the thing of choice when using generators-as-iterators, e.g.
when walking a tree. But what bout yield from for coroutines?)
- The cocall keyword eerily reminds me of Fortran. I know that's not
fair, but still...
- The syntax worries me. Your PEP suggests that cocall binds tightly
to an atom. That would mean that if the cofunction is really a
comethod, you'd have to parenthesis it, like cocall (obj.method)(args)
? Huuuu, ugly. Also things lke 'cocall foo' (no call syntax) weird me
out.
- How much of the improved error flagging of codef/cocall can be
obtained by judicious use of decorators and helper functions? (I need
this in Python 2.5 *now*. :-)
--Guido
On Tue, Aug 10, 2010 at 12:22 AM, Greg Ewing
<greg.ewing at canterbury.ac.nz> wrote:
> Guido van Rossum wrote:
>
>> We'll see. I still cannot get my head around why cofunctions are so
>> great.
>
> I think I can offer some evidence. I've been playing around with
> two versions of a discrete-event simulation kernel, one using
> yield-from and the other using cofunctions. Here's the main
> function of one of my test cases. I've introduced a deliberate
> bug -- can you spot it?
>
> def customer(i):
> print("Customer", i, "arriving at", now())
> yield from tables.acquire(1)
> print("Customer", i, "sits down at a table at", now())
> yield from waiters.acquire(1)
> print("Customer", i, "orders spam at", now())
> hold(random.normalvariate(20, 2))
> waiters.release(1)
> print("Customer", i, "gets served spam at", now())
> yield from hold(random.normalvariate(10, 5))
> print("Customer", i, "finished eating at", now())
> tables.release(1)
>
> The bug is that the first call to hold() is missing a 'yield
> from' in front of it. If I run this, I don't get any exception --
> it produces plausible-looking but incorrect results.
>
> Here's another version, with a very similar bug in a different
> place -- this time it's the second call to hold() that's missing
> a 'yield from'.
>
> def customer(i):
> print("Customer", i, "arriving at", now())
> yield from tables.acquire(1)
> print("Customer", i, "sits down at a table at", now())
> yield from waiters.acquire(1)
> print("Customer", i, "orders spam at", now())
> yield from hold(random.normalvariate(20, 2))
> waiters.release(1)
> print("Customer", i, "gets served spam at", now())
> hold(random.normalvariate(10, 5))
> print("Customer", i, "finished eating at", now())
> tables.release(1)
>
> If I run this one, I do get an exception, but it's a rather
> unhelpful one:
>
> Traceback (most recent call last):
> File "restaurant2.py", line 35, in <module>
> run()
> File
> "/Local/Projects/D/Python/YieldFrom/3.1/YieldFrom-3.1.2/Examples/Simulation/simulation.py",
> line 25, in run
> next(current_process)
> File "restaurant2.py", line 32, in customer
> tables.release(1)
> File
> "/Local/Projects/D/Python/YieldFrom/3.1/YieldFrom-3.1.2/Examples/Simulation/resource.py",
> line 25, in release
> wakeup(self.queue[0])
> File
> "/Local/Projects/D/Python/YieldFrom/3.1/YieldFrom-3.1.2/Examples/Simulation/simulation.py",
> line 34, in wakeup
> schedule(process, now())
> File
> "/Local/Projects/D/Python/YieldFrom/3.1/YieldFrom-3.1.2/Examples/Simulation/simulation.py",
> line 16, in schedule
> heappush(event_queue, (time, process))
> TypeError: unorderable types: generator() < generator()
>
> If you examine the traceback, you'll find that *nowhere* does it
> mention the location where the error actually is! Instead, a
> mysterious error emanates from some place deep inside the scheduler.
> I would hate to have to track down a problem like this in a large
> program.
>
> Here's the equivalent thing using cofunctions, complete with a
> corresponding missing 'cocall':
>
> codef customer(i):
> print("Customer", i, "arriving at", now())
> cocall tables.acquire(1)
> print("Customer", i, "sits down at a table at", now())
> cocall waiters.acquire(1)
> print("Customer", i, "orders spam at", now())
> cocall hold(random.normalvariate(20, 2))
> cocall waiters.release(1)
> print("Customer", i, "gets served spam at", now())
> hold(random.normalvariate(10, 5))
> print("Customer", i, "finished eating at", now())
> cocall tables.release(1)
>
> The exception and traceback resulting from this is crystal clear:
>
> Traceback (most recent call last):
> File "restaurant2.py", line 34, in <module>
> run()
> File
> "/Local/Projects/D/Python/YieldFrom/3.1/Cofunctions-3.1.2/Examples/Simulation/simulation.py",
> line 25, in run
> next(current_process)
> File "restaurant2.py", line 29, in customer
> hold(random.normalvariate(10, 5))
> TypeError: Cofunction must be called with cocall or costart
>
> If this doesn't convince you of the utility of cofunctions or
> something like them, I don't know what will.
>
> --
> Greg
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> http://mail.python.org/mailman/listinfo/python-ideas
>
--
--Guido van Rossum (python.org/~guido)
More information about the Python-ideas
mailing list