Re: [Python-ideas] if condition: break idiom

Slightly OT, but I think 'break' and 'continue' should be replaced with 'raise Break' and 'raise Continue' in Python 4000, just as we 'raise StopIteration' in generators today. This would be handy, because you could use it in functions called by a list comprehension: def while(x): if x > 10: raise Break else: return x [while(x) for x in range(20)] #produces [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Right now, you can do something this with list(generator expression) by raising StopIteration, but it is considered a hack, and it doesn't work with list comprehensions.

On Sat, Sep 20, 2008 at 11:41 PM, Carl Johnson <carl@carlsensei.com> wrote:
I imagine what you are thinking of is: """ def While(x): if x > 10: raise StopIteration else: return x list(While(x) for x in range(20)) """ This doesn't seem too horrific to me, and certainly doesn't merit adding Break and Continue as Exception-like objects. I feel this would just make it unbearably hard for beginners moving in from <every other language that has looping block constructs and uses break and continue as statements>. An alternate construct could be something like: """ def While(x): if x > 10: return None else: return x [w for w in (While(x) for x in range(20)) if w is not None] """ Of course, this looks silly in this example, but in a context where While were to do something nontrivial, I think it would be suitable. -- Cheers, Leif

The problem is that this version won't work if range(20) is replaced with itertools.count() or any other non-finite generator, whereas the raise Break version will. Another nice thing about using raise for loop control is that "raise Continue" can be used to skip elements: def even(x): if x % 2: raise Continue else: return x [even(x) for x in range(10)] # [0, 2, 4, 6, 8] This would have to be broken in two parts to do in current Python (and indeed, that might be the chief advantage of *not* adopting my proposal--TOOWTDI): def even(x): return not x % 2 [x for x in range(10) if even(x)] As for beginners in Python, I don't think learning to use "raise Continue" instead of "continue" will really be that hard. I think the main reason to reject my proposal is that complicated loops shouldn't be done in list comprehensions at all. They should be written out, like reduce expressions. Still, it might be convenient in some cases, and it would be a good chance to cut out unnecessary keywords. -- Carl

On Sat, Sep 20, 2008 at 10:06 PM, Carl Johnson <carl@carlsensei.com> wrote:
def foo(): for x in range(10): if even(x): print "Even!" else: print "Odd!" I would never expect a function could raise an exception which is silently swallowed up. Having break be a statement enforces locality, a good thing IMO. generator expressions may currently swallow up StopIteration, but I argue this is a bug. It may not be worth fixing, but it's a bug nonetheless. The sweet spot for list-comps and generator expressions is when they're *simple*. Once they start to get complicated or fancy (such as making them exit early) you should switch to an explicit for-statement; more lines really is more readable. -- Adam Olsen, aka Rhamphoryncus

Ron Adam wrote:
Maybe break should raise a StopIteration and continue should raise NextIteration?
The trouble with that is that semantically, right now, StopIteration gets caught only during the testing to see if the iteration should go for another loop around, not during the body of the loop. Here are some illustrations of the current behavior:
As you can see, it silently swallows the StopIteration raised when evaluating the iterator produced by blah().
Above, the exception isn't swallowed, since blah() hadn't yet been evaluated, since it's a function, not a generator. And if you try to use StopIteration like break, that just won't work:
This is why if my proposal were accepted we would need three loop controlling exceptions: StopIteration, to halt the loop while fetching the next value; ContinueIteration, to simulate the behavior of continue; and BreakIteration, to simulate the behavior of break. -- Carl

On Sat, Sep 20, 2008 at 9:09 PM, Ron Adam <rrr@ronadam.com> wrote:
You would have to essentially make loops syntactic sugar for try/except statements. And if you had StopIteration and NextIteration you would have to have a nested try/except:: try: LOOP: ... loop stuff ... try: ... loop body ... except NextIteration: pass goto LOOP except StopIteration: pass Exceptions are not expensive, but they are not exactly a no-op either. -Brett

On Sat, Sep 20, 2008 at 11:41 PM, Carl Johnson <carl@carlsensei.com> wrote:
I imagine what you are thinking of is: """ def While(x): if x > 10: raise StopIteration else: return x list(While(x) for x in range(20)) """ This doesn't seem too horrific to me, and certainly doesn't merit adding Break and Continue as Exception-like objects. I feel this would just make it unbearably hard for beginners moving in from <every other language that has looping block constructs and uses break and continue as statements>. An alternate construct could be something like: """ def While(x): if x > 10: return None else: return x [w for w in (While(x) for x in range(20)) if w is not None] """ Of course, this looks silly in this example, but in a context where While were to do something nontrivial, I think it would be suitable. -- Cheers, Leif

The problem is that this version won't work if range(20) is replaced with itertools.count() or any other non-finite generator, whereas the raise Break version will. Another nice thing about using raise for loop control is that "raise Continue" can be used to skip elements: def even(x): if x % 2: raise Continue else: return x [even(x) for x in range(10)] # [0, 2, 4, 6, 8] This would have to be broken in two parts to do in current Python (and indeed, that might be the chief advantage of *not* adopting my proposal--TOOWTDI): def even(x): return not x % 2 [x for x in range(10) if even(x)] As for beginners in Python, I don't think learning to use "raise Continue" instead of "continue" will really be that hard. I think the main reason to reject my proposal is that complicated loops shouldn't be done in list comprehensions at all. They should be written out, like reduce expressions. Still, it might be convenient in some cases, and it would be a good chance to cut out unnecessary keywords. -- Carl

On Sat, Sep 20, 2008 at 10:06 PM, Carl Johnson <carl@carlsensei.com> wrote:
def foo(): for x in range(10): if even(x): print "Even!" else: print "Odd!" I would never expect a function could raise an exception which is silently swallowed up. Having break be a statement enforces locality, a good thing IMO. generator expressions may currently swallow up StopIteration, but I argue this is a bug. It may not be worth fixing, but it's a bug nonetheless. The sweet spot for list-comps and generator expressions is when they're *simple*. Once they start to get complicated or fancy (such as making them exit early) you should switch to an explicit for-statement; more lines really is more readable. -- Adam Olsen, aka Rhamphoryncus

Ron Adam wrote:
Maybe break should raise a StopIteration and continue should raise NextIteration?
The trouble with that is that semantically, right now, StopIteration gets caught only during the testing to see if the iteration should go for another loop around, not during the body of the loop. Here are some illustrations of the current behavior:
As you can see, it silently swallows the StopIteration raised when evaluating the iterator produced by blah().
Above, the exception isn't swallowed, since blah() hadn't yet been evaluated, since it's a function, not a generator. And if you try to use StopIteration like break, that just won't work:
This is why if my proposal were accepted we would need three loop controlling exceptions: StopIteration, to halt the loop while fetching the next value; ContinueIteration, to simulate the behavior of continue; and BreakIteration, to simulate the behavior of break. -- Carl

On Sat, Sep 20, 2008 at 9:09 PM, Ron Adam <rrr@ronadam.com> wrote:
You would have to essentially make loops syntactic sugar for try/except statements. And if you had StopIteration and NextIteration you would have to have a nested try/except:: try: LOOP: ... loop stuff ... try: ... loop body ... except NextIteration: pass goto LOOP except StopIteration: pass Exceptions are not expensive, but they are not exactly a no-op either. -Brett
participants (5)
-
Adam Olsen
-
Brett Cannon
-
Carl Johnson
-
Leif Walsh
-
Ron Adam