[Python-ideas] Cofunctions - an enhancement to yield-from

Guido van Rossum guido at python.org
Mon Aug 2 18:39:01 CEST 2010


After mulling it over and trying to understand the thread I still
cannot get excited about this proposal. The only concrete objection I
have is that it might be hard to implement in Jython or IronPython --
IIRC we were careful to define yield in such a way that it was easy to
generate JVM bytecode for them, and IIRC the details of making it easy
had to do with the compiler breaking the generator function into
different entry points for each resumption point (i.e. after each
yield). In a codef you couldn't do this, since you don't know until
run time which calls are codef calls.

OTOH I do appreciate the desire to reduce the number of places where
one has to sprinkle 'yield' over one's code, and I've had a number of
situations recently where I had something that logically needed to be
a coroutine (to match some API) but just happened not to need any
yields, and inevitably my coding went something like (1) forget to put
a yield in, (2) frantically debug, (3) slap forehead, (4) add "if 0:
yield" to the function, (5) continue with another instance of this,
(6) lose sleep over the best place to spell the dummy yield and where
to put it. At the same time I don't want to have to mark all my
coroutines with a decorator, like Monocle requires (though maybe I
should).

Finally, regardless of what happens to codef, I am still
enthusiastically supporting PEP 380 as it stands, and am excited to
see it ported to Python 3.1 (though I hope that once we've done the
Mercurial switch, someone will create a branch for it to be merged
into 3.3).

--Guido

On Sun, Aug 1, 2010 at 3:09 AM, Greg Ewing <greg.ewing at canterbury.ac.nz> wrote:
> I've been thinking about this idea for a while, and now
> that I've got yield-from nailed down to my satisfaction,
> I thought I'd put it out there to see if anyone else
> thinks it would be a good thing.
>
> Cofunctions
> -----------
>
> A drawback of 'yield from' as a coroutine mechanism is that it must
> be used every time a function forming part of a coroutine calls
> another function that can suspend the coroutine, either directly
> or indirectly.
>
> This makes the code read somewhat awkwardly and provides many
> opportunities for the programmer to make errors. It also introduces
> considerable coupling, since changing one's mind about whether a
> function needs to be suspendable requires revisiting all the call
> sites of that function.
>
> This proposal builds on the 'yield from' proposal by introducing a
> new kind of function that I will call a "cofunction".
>
> A cofunction is a special kind of generator, with the following
> characteristics:
>
> - It is defined by using the keyword 'codef' in place of 'def'.
>
> - It is always a generator, even if it does not contain any yields.
>
> - Whenever a call is made inside a cofunction, it is done using a
>  special COCALL opcode. This first looks for a __cocall__ method
>  on the object being called. If present, it is expected to
>  return an iterable object, which is treated as though 'yield from'
>  had been performed on it.
>
>  If the object being called does not have a __cocall__ method,
>  or it returns NotImplemented, the call is made in the usual way
>  through the __call__ method.
>
> - Cofunctions themselves have a __cocall__ method that does the
>  same thing as __call__.
>
> Using these cofunctions, it should be possible to write coroutine code
> that looks very similar to ordinary code. Cofunctions can call both
> ordinary functions and other cofunctions using ordinary call syntax.
> The only special consideration is that 'codef' must be used to define
> any function that directly or indirectly calls another cofunction.
>
> A few subtle details:
>
> - Ordinary generators will *not* implement __cocall__. This is so
>  that a cofunction can e.g. contain a for-loop that iterates over
>  a generator without erroneously triggering yield-from behaviour.
>
> - Some objects that wrap functions will need to be enhanced with
>  __cocall__ methods that delegate to the underlying function.
>  Obvious ones that spring to mind are bound methods, staticmethods
>  and classmethods.
>
>  Returning NotImplemented is specified as one of the possible
>  responses of __cocall__ so that a wrapper can report that the
>  wrapped object does not support __cocall__.
>
> --
> 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