[Python-ideas] Possible PEP 380 tweak

Jacob Holm jh at improva.dk
Wed Oct 27 18:53:07 CEST 2010


On 2010-10-26 18:56, Guido van Rossum wrote:
> Now, if I may temporarily go into wild-and-crazy mode (this *is*
> python-ideas after all :-), we could invent some ad-hoc syntax for
> this pattern, e.g.:
> 
>   for value in yield:
>     <use value>
>   return <result>
> 
> IOW the special form:
> 
>   for <var> in yield:
>     <body>
> 
> would translate into:
> 
>   try:
>     while True:
>       <var> = yield
>       <body>
>   except GeneratorExit:
>     pass
> 
> If (and this is a big if) the
> while-True-yield-inside-try-except-GeneratorExit pattern somehow
> becomes popular we could reconsider this syntactic extension or some
> variant. (I have to add that the syntactic ice is a bit thin here,
> since "for <var> in (yield)" already has a meaning, and a totally
> different one of course. A variant could be "for <var> from yield" or
> some other abuse of keywords.

Hmm.  This got me thinking.  One thing I'd really like to see in python
is something like the "channel" object from the go language
(http://golang.org/).

Based on PEP 380 or Gregs new cofunctions PEP (or perhaps even without
any of them) it is possible to write a trampoline-based implementation
of a channel object with "send" and "next" methods that work as
expected.  One thing that is *not* possible (I think) is to make that
object iterable.  Your wild idea above gave me a similar wild idea of my
own.  An extension to the cofunctions PEP that would make that possible.

1) Define a new "coiterator" protocol, consisting of a new special
method __conext__, and a new StopCoIteration exception that the regular
StopIteration inherits from.  __conext__ should be a generator that
yields as many times as necessary, then either raises StopCoIteration or
returns a result (possibly by raising a StopIteration with a value).
Add a new built-in "conext" cofunction that looks for a __conext__
method instead of a __next__ method.

2) Define a new "coiterable" protocol, consisting of a new special
method __coiter__.  __coiter__ is a regular function and should return
an object implementing the "coiterator" protocol.  Add a new built-in
"coiter" function that looks for a __coiter__ method instead of an
__iter__ method.   (We could also make this a cofunction but for now I
don't see the point).

3) Make sure that the for-loop in a cofunction:

   for val in coiterable:
      <block>
   else:
      <block>

expands as:

   _it = coiter(coiterable)
   while True:
       try:
           val = cocall conext(_it)
       except StopCoIteration:
           break
       <block>
   else:
       <block>

Which is exactly the same as in a normal function, except for the use of
"coiter" and "cocall conext" instead of "iter" and "next", and the use
of StopCoIteration instead of StopIteration.

3a) Alternatively define a new syntax for "coiterating" that expands as
in 3 and whose use is an alternative indicator that this is a cofunction.


All this to make it possible to write a code like this:

def consumer(ch):
    for val in ch:
        cocall print(val) # XXX need a cocall somewhere

def producer(ch):
    for val in range(10):
        cocall ch.send(val)

def main()
    sched = scheduler()
    ch = channel()
    sched.add(consumer(ch))
    sched.add(producer(ch))
    sched.run()


Thoughts?

- Jacob



More information about the Python-ideas mailing list