PEP Idea: Syntactic sugar for StopIteration.
Hello Python developers. This is my first post on the list. I have been using Python for a while and I have been thinking about one feature I would like to see integrated into the language. I thought it could be a good topic for a PEP, so I decided to join the list and write about it. First problem: empty generators. Suppose you have to write an empty generator. You could do something like: def foo(): return yield 'never' Or something like: def foo(): yield iter([]).next() Or: def foo(): raise StopIteration() yield "never" There is an old thread discussing the diferent alternatives: http://mail.python.org/pipermail/python-list/2002-November/171588.html Of curse this is unpythonic. It violates the principle of: "There should be one-- and preferably only one --obvious way to do it". Instead, we have a bunch of inelegant solutions, and no one is the obvious one. Second problem: Explicit raise without explicit try-except. Take a look at this example: def lines(): for line in my_file: if some_error(): raise StopIteration() yield line yield 'end' for line in lines(): do_something() Even when the lines function contains an explicit raise statement, there is no explicit try-except block. Of curse, the StopIteration exception is implicitly caught when the generator is called, but this looks a bit confusing. In my opinion, every explicitly raised exception should be explicitly caught by a try-except block. The solution: yield break. The solution used in C# for these problems is the 'yield break' statement. In this way, the empty generator would look like: def foo(): yield break This would be the pythonic way of writing an empty generator. In the same way, every time you want to stop the generation you should call 'yield break', for example: def lines(): for line in my_file: if some_error(): yield break yield line yield 'end' Note that 'yield break' resembles the 'break' statement used in loops, while 'StopIteration' doesn't. 'yield break' is more orthogonal to the rest of the language. I am looking forward to seeing your opinions. Manuel Cerón.
Note that 'yield break' resembles the 'break' statement used in loops, while 'StopIteration' doesn't. 'yield break' is more orthogonal to the rest of the language.
I am looking forward to seeing your opinions.
-1 I do not find the meaning to be transparent and the proposal adds new syntax without adding functionality. Also, I do not find the "emtpy generator" use case to be even slightly motivating. If an empty iterator were needed for some reason, I would just write: iter([]) and be done with it. Raymond
I would prefer plain 'yield' to 'yield break' as a synonym for 'raise StopIteration'. But the raise stands out better visually as an exit point. The point about the raise not being explicitly caught is not valid since any generator could be used in the following code g = some_gen try: while True: process(g.next()) except StopIteration: whatever() where it is caught. Then under your proposal, there would be a catch without a throw ;-(. If there were a use case for empty generators, then iter() could be made to produce one, in analogy with int() returning 0, etc. But without a use case, 'iter()' is much more likely to be a bug and should be flagged as such. In the meanwhile, 'iter(())' is trivial to type. Terry Jan Reedy
2007/12/8, Raymond Hettinger
...the proposal adds new syntax without adding functionality.
That is indeed the definition of syntactic sugar [1]. Python is full
of that, for example the import statement.
2007/12/8, Paul Svensson
What is the problem that is not solved by iter(()) ?
'yield iter(()).next()' it's obscure, you can't know its purpose at first sight. There are other alternatives but none of them seems to be the "obvious way to do it". But that is the less important problem. The real problem is that raising StopIteration is not orthogonal. It is confusing for the reasons I exposed. Generators are something in the middle between a language feature and a framework/library. With 'yield break' they will be completely a language feature. [1] http://en.wikipedia.org/wiki/Syntactic_sugar Manuel.
In your example, why do you "raise StopIteration" instead just writing "return"?
----- Original Message -----
From: "Manuel Alejandro Cerón Estrada"
Manuel Alejandro Cerón Estrada wrote:
2007/12/8, Raymond Hettinger
: ...the proposal adds new syntax without adding functionality.
That is indeed the definition of syntactic sugar [1]. Python is full of that, for example the import statement.
2007/12/8, Paul Svensson
: What is the problem that is not solved by iter(()) ?
'yield iter(()).next()' it's obscure, you can't know its purpose at first sight. There are other alternatives but none of them seems to be the "obvious way to do it".
But surely iter(()) itself (and indeed iter(any empty sequence) is precisely the empty generator you seek, without having to use a def statement to create your own in Python.
for i in iter(()): ... print "Result" ...
So I don't see what's so magical about creating your own empty iterator, nor why you consider it necessary. The 2002 discussion to which you refer is clearly outdated.
But that is the less important problem. The real problem is that raising StopIteration is not orthogonal. It is confusing for the reasons I exposed. Generators are something in the middle between a language feature and a framework/library. With 'yield break' they will be completely a language feature.
It's necessary, in discussions of syntactic sugar, to admit from the start that a certain amount of sweetness is desirable. If you would really like to remove the import statement you might consider programming in brainfuck, one of the least sugary languages around. http://en.wikipedia.org/wiki/Brainfuck Of course, Python is popular because it hits many people's "sweet spot" for the desirable balance between syntactic sugar and minimalism, making it possible to write compact solutions to advanced problems. There are definitely limits, though. "yield break" seems ugly and unnecessary to me. I'd rather consider Icon's "break break" to exit two levels of looping, but I don't see that flying either. You still haven't made a convincing use case for "yield break", IMHO. regards Steve -- Steve Holden +1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/
2007/12/8, Raymond Hettinger
In your example, why do you "raise StopIteration" instead just writing "return"?
In the example would be better to use 'return' rather than 'raise StopIteration'. In most cases 'return' will have the same behavior than 'raise StopIteration', but not always. Acording to PEP 255: Note that return isn't always equivalent to raising StopIteration: the difference lies in how enclosing try/except constructs are treated. The trivial example is the empty generator where return is ambiguous with the same statement for normal functions. Maybe my proposal can change to "Replace 'return' with 'yield break' in generators" Again, PEP 255 talks about the possibility of removing 'return' from generators Q. Why allow "return" at all? Why not force termination to be spelled "raise StopIteration"? A. The mechanics of StopIteration are low-level details, much like the mechanics of IndexError in Python 2.1: the implementation needs to do *something* well-defined under the covers, and Python exposes these mechanisms for advanced users. That's not an argument for forcing everyone to work at that level, though. "return" means "I'm done" in any kind of function, and that's easy to explain and to use. Note that "return" isn't always equivalent to "raise StopIteration" in try/except construct, either (see the "Specification: Return" section). Of curse, the problem of low level details it's solved by 'yield break' Manuel.
2007/12/8, Raymond Hettinger
: ...the proposal adds new syntax without adding functionality.
That is indeed the definition of syntactic sugar [1]. Python is full of that, for example the import statement. . . . The real problem is that raising StopIteration is not orthogonal. It is confusing for the reasons I exposed. Generators are something in the middle between a language feature and a framework/library. With 'yield break' they will be completely a language feature.
This personal notion of purity carries almost zero weight and is more a matter of personal taste/style than anything else. It certainly does not warrant a change to the grammar. I've been a big fan of generators and tend to use them everywhere. I've *never* had a use case for "yield break" and find it uncommon to even "raise StopIteration". The tone of your messages suggests that you're going to stick to this idea like glue, so to avoid further time wasting, this will likely be my last post on the subject. If for some reason, you persist through to writing a PEP, it will be almost mandatory to scan existing, published code to find examples of code fragments that would be improved by 'yield break' . My bet is that the examples will be rare and any "improvement" will be marginal at best and simply a matter of taste at worst. Raymond
Manuel Alejandro Cerón Estrada wrote:
The real problem is that raising StopIteration is not orthogonal.
This is a non-problem as far as I can see. In a generator, the way to stop the iteration is simply to return. In a non-generator iterator, you're still going to have to raise StopIteration. So I see no advantage at all to this proposal. -- Greg
Manuel Alejandro Cerón Estrada wrote:
Acording to PEP 255:
Note that return isn't always equivalent to raising StopIteration: the difference lies in how enclosing try/except constructs are treated.
All that means is that def g(): try: if 0: yield return except StopIteration: print "Spam" won't print "Spam". But since this involves catching the exception with an explicit try-except, it doesn't fall under the scope of your complaint.
Of curse, the problem of low level details it's solved by 'yield break'
I would put it the other way around -- the problem that 'yield break' is meant to solve is already solved by 'return'. So there's no need for change. -- Greg
2007/12/8, Greg Ewing
I would put it the other way around -- the problem that 'yield break' is meant to solve is already solved by 'return'. So there's no need for change.
I have been re-thinking the problem and this is true. The only exception would be empty generators, but they are rare. 'return' works well for 99.99% of the cases. And the rest 0.01% does not worth a syntax change. Thank you very much for all your comments. Manuel.
participants (6)
-
Greg Ewing
-
Manuel Alejandro Cerón Estrada
-
Phillip J. Eby
-
Raymond Hettinger
-
Steve Holden
-
Terry Reedy