[Python-ideas] for/else statements considered harmful
Nick Coghlan
ncoghlan at gmail.com
Thu Jun 7 02:53:22 CEST 2012
On Thu, Jun 7, 2012 at 9:58 AM, Bruce Leban <bruce at leapyear.org> wrote:
> If we could go back in time I would completely agree. But since we can't,
> flipping meaning of else would be too error inducing and therefore not at
> all likely.
The meaning of the "else:" clause on for and while loops is actually
much closer to the sense in "try/except/else" sense than it is to the
sense in "if/else".
Consider the following:
for x in range(20):
if x > 10:
break
else:
# Reached the end of the loop
As an approximate short hand for:
class BreakLoop(Exception): pass
try:
for x in range(20):
if x > 10:
raise BreakLoop
except BreakLoop:
pass
else:
# Reached the end of the loop
It's not implemented anything like that (and the analogy doesn't hold
in many other respects), but in terms of the semantics of the
respective else clauses it's an exact match.
Part of the problem is that the "else:" clause on while loops is often
explained as follows (and I've certainly been guilty of this), which I
now think exacerbates the confusion rather than reducing it:
The following code:
x = 0
while x < 10:
x += 1
if x == y:
break
else:
# Made it to 10
Can be seen as equivalent to:
x = 0
while 1:
if x < 10:
pass
else:
# Made it to 10
x += 1
if x == y:
break
This actually ends up reinforcing the erroneous connection to if
statements, when we really need to be encouraging people to think of
this clause in terms of try statements, with "break" playing the role
of an exception being raised.
So I think what we actually have is a documentation problem where we
need to be actively encouraging the "while/else", "for/else" ->
"try/except/else" link and discouraging any attempts to think of this
construct in terms of if statements (as that is a clear recipe for
confusion).
If anything were to change at the language level, my preference would
be to further reinforce the try/except/else connection by allowing an
"except break" clause:
for x in range(20):
if x > 10:
break
except break:
# Bailed out early
else:
# Reached the end of the loop
To critique the *specific* proposal presented at the start of the
thread, there are three main problems with it:
1. It doesn't match the expected semantics of a "finally:" clause. In
try/finally the finally clause executes regardless of how the suite
execution is terminated (whether via an exception, reaching the end of
the suite, or leaving the suite early via a return, break or continue
control flow statement). That is explicitly not the case here (as a
loop's else clause only executes in the case of normal loop
termination - which precisely matches the semantics of the else clause
in try/except/else)
2. As Bruce pointed out, the meaning of the else: clause on loops
can't be changed as it would break backwards compatibility with
existing code
3. The post doesn't explain how the proposed change in semantics also
makes sense for while loops
Cheers,
Nick.
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
More information about the Python-ideas
mailing list