[Python-ideas] Is this PEP-able? for X in ListY while conditionZ:

Andrew Barnert abarnert at yahoo.com
Sun Jun 30 13:41:27 CEST 2013


From: Nick Coghlan <ncoghlan at gmail.com>

Sent: Sunday, June 30, 2013 12:51 AM


> On 30 June 2013 16:43, Andrew Barnert <abarnert at yahoo.com> wrote:
>>  From: Nick Coghlan <ncoghlan at gmail.com>
>>>  People already have to learn "for/else" and 
> "while/else".
>>>  Adding
>>>  "break if" can *only* be justified on the grounds of pairing 
>>> it up
>>>  with those two existing else clauses to make appropriate if/else
>>>  pairs, as "for/break if/else" and "while/break 
>>> if/else"
>>>  should
>>>  actually be easier to learn than the status quo.
>> 
>>  I honestly don't think it would read this way to most people; the else 
>> clause would still be confusing. Especially if it continues to means the same 
>> thing even without break if, which means as soon as you "learn" the 
>> for/break if/else rule you'll learn that it's not really true.
> 
> Hence why I said any such PEP should also propose that a dangling
> "else" on a loop without a break statement should be deprecated and
> eventually become a syntax error. 

Sure, but we already have a way to put a break into a for loop: the break statement. Unless you're also planning to deprecate that, it will still be true that the "for/break if/else" rule is often not true. For example:

    for i in x:

        if i > 0:
            print(i)
        else:
            break
    else:
        print('Finished')

Also, even in your preferred case, this would be the only place in Python where an statement matches another statement at a different indentation level. Is that really going to make Python easier to understand?


And finally, as others have pointed out, besides being yet another syntax for if (if statements, ternary expressions, if clauses, and now break if statements and clauses), what it really reads like is perl's postfix if, which is misleading, because you can't do postfix conditionals anywhere else.

Killing two birds with one stone is nice, but I don't think this kills either bird.

>>  Also, if it comes at the cost of making comprehensions harder to learn and 

>> understand, I think people use (and see) comprehensions much more often than 
>> for/else.
> 
> While it does have a strong statement/expression dichotomy, Python is
> still one language, not two. Any proposals that rely on adding new
> expression-only keywords are dead in the water. 

I think I didn't make my point very clearly. I'm _assuming_ that Python is one language. As a consequence, if some new loop syntax doesn't work well in expressions, it's not worth doing, even if it _does_ work well in statements. Also, the syntax has to follow the regular mapping between clauses and statements—each clause is a nested block statement—or it's not worth doing.

I realize that you have a solution for the second point: the semicolon-separated "comp_break_if" clause at the end of a comprehension is clearly different, and therefore it's acceptable that it maps differently. But that means having two mapping rules instead of one, which still makes comprehensions twice as complicated.

> PEP 315 has now been
> explicitly rejected: the official syntax for terminating a loop early
> is the existing break statement, thus any proposal for terminating a
> comprehension early must also be based on break if it is to be
> considered a serious suggestion


And, as I said in my earlier summary, I think that means there is no feasible suggestion. The only option I like is #1 (redefining comps on top of genexprs), and I like it for completely independent reasons. (In fact, even with #1, I still probably wouldn't use stop() in listcomps.)

Just as in the language we borrowed listcomps from, takewhile is the right answer. You can stick it around the inner iterator, or one of the sub-iterators in a nested comprehension; there are no possible syntax ambiguities; there's nothing special to learn; etc.

The only real problem with takewhile is the same problem as map, filter, reduce, and everything in itertools: Python's lambda syntax sometimes makes it a bit clumsy to turn an expression into a function. And the solution is the same as it is everywhere else in Python: define the function out of line, move the takewhile call itself out of line, find somewhere else to break the expression up somewhere else, use a partial instead of a lambda, or just give up on trying to write an expression and write a statement. (Or push for PEP 403.)

It was worth exploring whether there are any obvious options—after all, we did reduce the need for map and filter calls, maybe we could do the same for takewhile—but that doesn't mean we have to pick the least bad if all of them are bad, it just means the answer is no.

And I think at this point, your earlier suggestion of drafting a PEP to get all of the bad ideas explicitly rejected is a better use of time than trying to draft a PEP to push for one of them.


More information about the Python-ideas mailing list