[Python-ideas] Cofunctions: It's alive! Its alive!
Greg Ewing
greg.ewing at canterbury.ac.nz
Tue Aug 10 09:22:31 CEST 2010
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
More information about the Python-ideas
mailing list