[Python-ideas] Cofunctions - Back to Basics
Steven D'Aprano
steve at pearwood.info
Thu Oct 27 14:19:31 CEST 2011
Greg Ewing wrote:
> Still feeling in a peppish mood after the last round of PEP 335,
> I decided to revisit my cofunctions proposal. Last time round, I
> allowed myself to be excessively swayed by popular opinion, and
> ended up with something that lacked elegance and failed to address
> the original issues.
>
> So, I've gone right back to my initial idea. The current version
> of the draft PEP can be found here:
>
> http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cd_current/cofunction-pep-rev5.txt
As per your later email, there is a newer revision here:
http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cd_current/cofunction-pep-rev6.txt
> together with updated examples here:
>
> http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cd_current/Examples/
As a PEP goes, this makes way too many assumptions about the reader's
knowledge of a small corner of Python. Coroutines themselves are
relatively new, and hardly in wide-spread use; yield from hasn't even
made it into a production version of Python yet. But adding new syntax
and built-ins will effect *all* Python programmers, not just the few who
use coroutines.
The rationale section is utterly unpersuasive to me. You talk about
cofunctions being a "streamlined way of writing generator-based
coroutines", but you give no reason other than your say so that it is
more streamlined. How about comparing the same coroutine written with,
and without, a cofunction?
You also claim that this will allow "the early detection of certain
kinds of error that are easily made when writing such code", but again
no evidence is given. You should demonstrate such an error, and the
"hard-to-diagnose symptoms" it causes, and some indication whether these
errors happen in practice or only in theory.
The Cofunction Definitions section does not help me understand the
concept at all:
(1) You state that it is a "special kind of generator", but don't give
any clue as to how it is special. Does it do something other generators
can't do? A cofunction is spelled 'codef' instead of 'def', but what
difference does that make to the behaviour of the generator you get? How
does it behave differently to other generators?
(2) Cofunctions, apparently, "may" contain yield or yield from.
Presumably that means that yield is optional, otherwise it would be
"must" rather than "may". So what sort of generator do you get without a
yield? The PEP doesn't give me any clue.
If I look at the external examples (the PEP doesn't link to them), I see
plenty of examples of syntax, but nothing about semantics. E.g.:
codef parse_elem(opening_tag):
name = opening_tag[1:-1]
closing_tag = "</%s>" % name
items = parse_items(closing_tag)
return (name, items)
What does this actually do?
(3) Something utterly perplexing to me:
"From the outside, the distinguishing feature of a cofunction is that it
cannot be called directly from within the body of an ordinary function.
An exception is raised if such a call to a cofunction is attempted."
Many things can't be called as functions: ints, strings, lists, etc. It
simply isn't clear to me how cofunctions are different from any other
non-callable object. The PEP should show some examples of what does and
doesn't work. In particular, the above as stated implies the following:
def spam():
x = cofunction() # fails, since directly inside a function
x = cofunction() # succeeds, since not inside a function
Surely that isn't what you mean to imply, is it?
Is there any prior art in other languages? I have googled on
"cofunction", and I get many, many hits to the concept from mathematics
(e.g. sine and cosine) but virtually nothing in programming circles.
Apart from this PEP, I don't see any sign of prior art, or that
cofunction is anything but your own term. If there is prior art, you
should describe it, and if not, you should say so.
Based on the PEP, it looks to me that the whole premise behind
"cofunction" is that it allows the user to write a generator without
using the word yield. That *literally* the only thing it does is replace
a generator like this:
def coroutine():
yield from f()
with
codef coroutine():
f()
with some syntactic sugar to allow Python to automagically tell the
difference between ordinary function calls and cofunctions inside a
cofunction: "Do What I Mean" functionality.
If cofunctions actually are more than merely a way to avoid writing
yield, you should explain how and why they are more. You should also
explain why the form
yield from f() # function call
is so special that it deserves a new keyword and a new built-in, while
this otherwise semantically identical call:
x = f()
yield from x # here's one we prepared earlier
does not.
In the Motivation and Rationale section, you state:
If one forgets to use ``yield from`` when it should have
been used, or uses it when it shouldn't have, the symptoms
that result can be extremely obscure and confusing.
I'm not sympathetic to the concept that "remembering which syntax to use
is hard, so let's invent even more syntax with non-obvious semantics".
I don't believe that remembering to write ``codef`` instead of ``def``
is any easier than remembering to write ``yield from`` instead of
``yield`` or ``return``.
--
Steven
More information about the Python-ideas
mailing list