| Store | Cart

Re: generator/coroutine terminology

From: Chris Angelico <ros...@gmail.com>
Tue, 17 Mar 2015 00:16:27 +1100
On Mon, Mar 16, 2015 at 10:51 PM, Steven D'Aprano
<stev...@pearwood.info> wrote:
> Chris Angelico wrote:> Just for the record, it's not just name bindings that make a generator> behave as a coroutine. E.g.:>> py> def co():> ...     print( (yield 1) + 2 )> ...> py> a = co()> py> next(a)> 1> py> a.send(98)> 100> Traceback (most recent call last):>   File "<stdin>", line 1, in <module>> StopIteration

Yep, I agree. It's not the name binding, it's whether the yield
expression's result is instantly discarded vs used in some way.

> It's surprising to me that you can send() into a non-coroutine and have it> ignored. I'm not even sure that is documented behaviour or just an accident> of implementation.

I agree conceptually; but there are two different consistencies here,
and they clash, my lords, they clash! One is that the API for a
coroutine includes send() but the API for a non-coroutine generator
doesn't, and that trying to send() into the latter should raise an
error. (Side point: What error? I'd accept AttributeError, but since
it's possible to mix sendable and nonsendable yields, it might be
better to have it raise at the time of the call - that is, when you
send a non-None value into a generator that's just going to discard
the value, it raises on arrival. In that case, possibly ValueError?
GeneratorStyleError?) And on the other hand, we have a very strong
Python convention that any expression can be used as a statement
without altering the effect of that expression in any way. Assigning
something to a name that you never use, or passing it as an argument
to a no-op function, should not suddenly change the behaviour of
anything.

>> That said, though, it would be quite reasonable for a *linter* to warn>> you about sending into a generator that never uses sent values. With a>> good type inference system (not sure if MyPy is sufficient here), it>> would be possible to distinguish between those two functions and>> declare that the first one has the return type "sendable_generator">> and the second one "nonsendable_generator", and give a warning if you>> send into the latter. But I don't think that's the language's job.>> I do :-)>> I think it would be hard for a linter to do this. It can't just do a> type-check, because the types of generator-iterators and> generator-coroutines are the same. It would need to actually analyse the> source code, AST, or byte-code. That's doable, but the compiler already> does that, and compiles different code for the two cases:>> py> from dis import dis> py> def g1():> ...     yield 1> ...> py> def g2():> ...     x = yield 1> ...> py> dis(g1)>   2           0 LOAD_CONST               1 (1)>               3 YIELD_VALUE>               4 POP_TOP>               5 LOAD_CONST               0 (None)>               8 RETURN_VALUE> py> dis(g2)>   2           0 LOAD_CONST               1 (1)>               3 YIELD_VALUE>               4 STORE_FAST               0 (x)>               7 LOAD_CONST               0 (None)>              10 RETURN_VALUE>>> so it should be easy for the compiler to recognise a yield used as if it> were a statement versus one used as an expression, and take appropriate> steps. But maybe there are subtle corner cases I haven't thought of.

That code isn't actually all that different, though. What you have is this:

1) Load up a value
2) Yield, which pops the value off, yields it, and pushes the sent value or None
3a) in g1: Discard the value of the expression that we've just finished
3b) in g2: Store the value of that expression into x
4) Load None, and return it.

Steps 1, 3, and 4 would all be identical if you replace the "yield 1"
with, say, "print(1)". It'll pop the 1 off, do something, and leave a
result on the stack. The difference between assignment and
non-assignment comes later.

The risk I see here is that a simple optimization might suddenly make
a semantic change to something. Imagine this function:

def gen():
    while True:
        x = (yield 1)
        if x == 5: break

Okay, so it cares about sent values. Well and good. Then we change it
so it doesn't always care:

def gen(state):
    while True:
        x = (yield 1)
        if state and x == 5: break

Ahh but look! That's a mode-switch parameter. We should break that out
into two functions.

def gen_stoppable():
    while True:
        x = (yield 1)
        if x == 5: break

def gen_unstoppable():
    while True:
        x = (yield 1)

Now, according to the current rules, you can change that last line to
just "yield 1" without the two functions differing in API. There's no
sent value that will stop the second one, but it will accept and
ignore sent values, just as the first one does. (Imagine that's some
sort of password, and the second generator is for the case where the
account is locked and there IS no password.) But according to your
rules, changing it to no longer assign the yielded value somewhere
would make a semantic and API change to the function. That seems, to
me, at least as surprising as the case of send() working when the
value's going to be ignored.

Either way, it's not going to be ideal. I do hear you about the
problem of sending when you shouldn't be able to... but I'm not
convinced the cure isn't worse than the disease.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list

Recent Messages in this Thread
Rustom Mody Mar 12, 2015 01:35 pm
Chris Angelico Mar 12, 2015 01:55 pm
brea...@gmail.com Mar 12, 2015 01:57 pm
Steven DAprano Mar 12, 2015 04:27 pm
Rustom Mody Mar 12, 2015 04:52 pm
Marko Rauhamaa Mar 12, 2015 05:55 pm
Rustom Mody Mar 13, 2015 02:23 am
Steven DAprano Mar 13, 2015 03:30 am
Rustom Mody Mar 13, 2015 05:28 am
Chris Angelico Mar 13, 2015 08:23 am
Rustom Mody Mar 13, 2015 09:12 am
Marko Rauhamaa Mar 13, 2015 09:36 am
Steven DAprano Mar 14, 2015 06:04 am
Marko Rauhamaa Mar 14, 2015 07:54 am
Mark Lawrence Mar 14, 2015 08:04 am
Ian Kelly Mar 14, 2015 08:14 pm
Mark Lawrence Mar 14, 2015 08:31 pm
Rustom Mody Mar 15, 2015 04:15 am
Marko Rauhamaa Mar 14, 2015 08:30 am
Rustom Mody Mar 14, 2015 03:29 pm
Chris Angelico Mar 14, 2015 03:56 pm
Rustom Mody Mar 14, 2015 03:59 pm
Chris Angelico Mar 14, 2015 04:14 pm
Rustom Mody Mar 14, 2015 04:33 pm
Chris Angelico Mar 14, 2015 04:51 pm
Dave Angel Mar 14, 2015 05:07 pm
Mark Lawrence Mar 14, 2015 04:56 pm
Rustom Mody Mar 14, 2015 05:17 pm
Steven DAprano Mar 15, 2015 08:37 am
Chris Angelico Mar 13, 2015 11:32 am
Oscar Benjamin Mar 14, 2015 10:02 pm
Marko Rauhamaa Mar 14, 2015 10:15 pm
Chris Angelico Mar 14, 2015 10:24 pm
Marko Rauhamaa Mar 15, 2015 12:15 am
Chris Angelico Mar 15, 2015 12:22 am
Steven DAprano Mar 16, 2015 01:03 am
Marko Rauhamaa Mar 16, 2015 07:12 am
Chris Angelico Mar 16, 2015 07:21 am
Ian Kelly Mar 16, 2015 07:37 am
Steven DAprano Mar 16, 2015 08:36 am
Chris Angelico Mar 16, 2015 08:58 am
Marko Rauhamaa Mar 16, 2015 12:32 pm
Rustom Mody Mar 16, 2015 12:51 pm
Marko Rauhamaa Mar 16, 2015 01:13 pm
Steven DAprano Mar 16, 2015 02:32 pm
Ian Kelly Mar 16, 2015 02:45 pm
Steven DAprano Mar 16, 2015 01:39 pm
Rustom Mody Mar 16, 2015 02:19 pm
Mark Lawrence Mar 16, 2015 02:26 pm
Steven DAprano Mar 16, 2015 02:35 pm
Steven DAprano Mar 16, 2015 02:36 pm
Rustom Mody Mar 16, 2015 02:37 pm
Rustom Mody Mar 16, 2015 02:55 pm
Mark Lawrence Mar 16, 2015 06:19 pm
Rustom Mody Mar 17, 2015 02:52 am
Mark Lawrence Mar 17, 2015 03:07 am
Rustom Mody Mar 17, 2015 03:18 am
Ian Kelly Mar 16, 2015 02:52 pm
Marko Rauhamaa Mar 16, 2015 03:09 pm
Ian Kelly Mar 16, 2015 03:26 pm
Marko Rauhamaa Mar 16, 2015 04:05 pm
Steven DAprano Mar 16, 2015 11:51 am
Chris Angelico Mar 16, 2015 01:16 pm
Marko Rauhamaa Mar 16, 2015 07:52 am
Steven DAprano Mar 16, 2015 12:02 pm
Jonas Wielicki Mar 16, 2015 12:39 pm
Marko Rauhamaa Mar 16, 2015 12:42 pm
Marko Rauhamaa Mar 16, 2015 07:40 am
Steven DAprano Mar 16, 2015 11:59 am
Marko Rauhamaa Mar 15, 2015 12:48 am
Chris Angelico Mar 15, 2015 02:02 am
Terry Reedy Mar 12, 2015 08:11 pm
Mark Lawrence Mar 17, 2015 03:55 am
Albert van der Horst Mar 31, 2015 12:57 pm
Albert van der Horst Mar 31, 2015 01:18 pm
Dave Angel Mar 31, 2015 01:38 pm
Steven DAprano Apr 03, 2015 06:02 am
Paul Rubin Apr 03, 2015 06:46 am
Albert van der Horst Mar 31, 2015 03:03 pm
Chris Angelico Mar 31, 2015 03:36 pm
Rustom Mody Mar 17, 2015 03:33 am
Mark Lawrence Mar 17, 2015 03:25 am
Marko Rauhamaa Mar 12, 2015 08:22 pm
Messages in this thread