On Nov 21, 2014, at 9:41, Antony Lee email@example.com wrote:
I would like to believe that "break"-as-a-expression solves most of these issues.
This one has been raised every time in the past. Since we don't have the PEP, I'll try to summarize the problems. But first:
It is reasonable, though, to drop the "return"-as-an-expression part of the proposal, because you need to know that the comprehension is run in a separate function to understand it (it's a bit ironic that I am dropping the original proposal to defend my own, now...).
There's a more significant difference between the two proposals. A break expression allows you to break out of a single loop, but gives you no way to break out of the whole thing (as your example shows). A return expression allows you to break out of the whole thing, but gives you no way to break out of a single loop. That's why they're not interchangeable in explicit loop code. (Also, notice that every so often, someone proposed a numbered or labeled break, and the answer is always "Why would you need that? If your code has enough loops that you need to break out of some but not all, refactor those some into a separate function and then use return.")
This also raises the question of why one of break/continue/return should be an expression but not the others. If your answer is "because you only need continue when there's code not controlled by the if, which is impossible in a comprehension" then you're pretty much admitting that the flow control expression thing is not really a general purpose thing, but a hack made for comprehensions.
((x, y) for x in l1 if f1(x) or break for y in l2 if f2(y) or break)
This maps directly to
for x in l1: if f1(x) or break: for y in l2: if f2(y) or break: yield x, y
When you write it this way, it's pretty clear that you're abusing or as an else, and that it comes in the wrong place, and that you're cramming side effects into an expression with no value. Would you put anything else with side effects here, even a call to a logging function?
You're also introducing unnecessary indentation, and making the code more verbose by comparison with the obvious alternative:
for x in l1: if not f1(x): break for y in l2: if not f2(y): break yield x, y
Yes, that alternative can't be written as a comprehension. But that doesn't mean we should come up with a less obvious, more verbose, and harder to reason about alternative just because it can be written as a comprehension, even though we'd never write it as an explicit loop. If you want to go that route, we might as well just enshrine the "or stop()" hack instead of trying to break it.
Also, can you imagine using break as an expression in any other context besides as an or operand in an if statement directly under a for statement?