On 2010-10-29 09:18, Greg Ewing wrote:
I've been pondering the whole close()-returning-a-value thing I've convinced myself once again that it's a bad idea.
And I still believe we could have made it work. However, I have been doing my own thinking about the whole of PEP 380, PEP 3152, for-loop co-iteration and so on. And I think I have an idea that improves the whole story.
The main thing to note is that the expression form of yield-from is mostly intended to make it easier to have cofunctions that return a value, and that there is a problem with reusing StopIteration for that purpose. Now that we have an actual PEP 3152, we could choose to move the necessary support over there. Here is one way to do that:
1) Drop the current PEP 380 support for using "return <value>" inside a generator. That means no extended StopIteration and no expression form of "yield from". And since there are no return values, there is no problem with how "close" should treat them.
2) In PEP 3152, define "return <value>" in a cofunction to raise a new IterationResult exception with the value. (And treat falling off the edge of the function or returning without a value as "return None")
3) In PEP 3152, change the "cocall" expansion so that:
<val> = cocall f(*args, **kwargs)
try: yield from f.__cocall__(*args, **kwargs) except IterationResult as e: <val> = e.value else: raise StopIteration
(The same expansion would be used if cocalls are implicit of course). This ensures that a cofunction can raise StopIteration just as a regular function, which means we can extend the iterator protocol to support cofunctions with only minor changes.
An interesting variation might be to keep the expression form of yield-from, but change its semantics so that it returns the StopIteration instance that was caught, instead of trying to extract a value. Then adding an IterationResult inheriting from StopIteration and using it for "return <value>" in a generator.
That would make all current yield-from examples work with the minor change that the old:
<var> = yield from <expr>
would need to be written as
<var> = (yield from <expr>).value
And would have the benefit that the PEP 3152 expansion could reraise the actual StopIteration as in:
e = yield from f.__cocall__(*args, **kwargs) if isinstance(e, IterationResult): <var> = e.value else: raise e
The idea of returning the exception takes some getting used to, but it solves the problem with StopIteration and cofunctions, and I'm sure I can find some interesting uses for it by itself.