[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