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