for / while else doesn't make sense

Steven D'Aprano steve at pearwood.info
Sat May 21 22:57:15 EDT 2016


On Sun, 22 May 2016 06:48 am, Erik wrote:

> Let me tell you a story ;) <wibbly-wobbly-lines> Back in the mid-to-late
> 1980s I worked with C compilers on hardware that could take several
> minutes to compile even a fairly trivial program. They errored on
> syntactically incorrect code and happily compiled syntactically correct
> code. Sometimes the output of the compiler wouldn't execute as expected
> because of "undefined behaviour" of some parts of the language (which
> the compilers could quite legally accept but would not produce valid
> code for - even illegal ops are fair game at that point). They would
> create valid code for the valid syntax of buggy code ("if (x = y) {
> foo(); }") without a whimper.

Don't get me started about C and undefined behaviour.

Fortunately, Python has nothing even remotely like C undefined behaviour.


> At that time, we had a program called 'lint'.
[...]
> And now, today, the compilers all do far more than the original 'lint'
> program did in almost zero time every time some source is compiled. It
> is free; it is not something one has to remember to run every so often. 

This is certainly not the case for Python. With C, you run your
compiler+linter once, and it builds an executable which can then run
without the compiler or linter.

With Python, *every time you run* the code, the compiler runs. The compiler
is the interpreter. It can, sometimes, skip some of the compilation steps
(parsing of source code) by use of cached byte-code files, but not all of
them. Including a linter will increase the size of the compiler
significantly, which much be distributed or installed for even the smallest
Python script, and it will have runtime implications re compilation time,
execution time, and memory use.

If you make the linter optional, say, a Python module that you install
separately and run only if you choose, then you have the status quo.


> So, back to Python ;)
> 
> The responses of "we can all write suspect/bad/ineffectual code - so
> just run the linter" takes me back those 30 years to when we HAD to do
> that with our C code too ...
> 
> There must be a better way.

Yes. And the better way is... don't write a language where you NEED a linter
because the language specification is so fecking *insane* that no human
being can reliably write correct code without running into undefined
behaviour which *can and will* have effects that propagate in both
directions, contaminating code which is correct in unpredictible ways.

https://blogs.msdn.microsoft.com/oldnewthing/20140627-00/?p=633/

One difference between C and Python is that most of the things which Python
linters look at don't actually have implications for correctness. Here are
a few of the features that Pylint looks at:

* line length;
* variable naming standards;
* unused imports;
* unnecessary semi-colons;
* use of deprecated modules;

and a complete list here:

http://pylint-messages.wikidot.com/all-codes

As you can see, most of the actual errors Pylint will pick up would result
in a runtime error, e,g, opening a file with an invalid mode. Most codes
are for code quality issues, related to maintenance issues, not bug
detection.

Coming back to for...else with no break, the behaviour is perfectly
well-defined, and does exactly what it is documented as doing. It's hard to
see that it is a "bug" for something to do what exactly what it is designed
to do. If somebody, for their own idiosyncratic reasons, wants to write:

for x in seq:
    spam()
else:
    eggs()

(Note: I've done this -- see below.)

and the language insists on a break, they will just pointlessly defeat the
compiler:

shutup_stupid_compiler = False
for x in seq:
    if shutup_stupid_compiler: break
    spam
else:
    eggs


Thus entering an arms race where the compiler is seen as something to be
silenced rather than something that helps you.

I said that I've written for...else with no break. Why would I do such a
thing? Because I wanted to clearly mark that the code in the else was a
unit of code that went with the for-loop.

Code displays varying levels of cohesiveness. Python lets you group related
code in four levels:

- the function/method;
- the class;
- the module;
- the package


but sometimes you have code which is smaller than a function that needs to
be considered as a unit, but is not enough to justify putting it into a
function. When that happens, we usually delimit it with comments, or
sometimes even just a blank line:

    code
    that
    goes
    together

    different
    bunch
    of
    code
    that
    goes
    together


So, when I had a bunch of code that included a for-loop, and something
immediately after the for-loop, I could have written:


    for x in seq:
        block
    code that goes
    with the loop

    different bunch
    of code


but I thought it communicated the grouping better to write it as:

    for x in seq:
        block
    else:  # runs after the for
        code that goes
        with the loop
    different bunch
    of code


I've since changed my mind. That's too subtle and too idiosyncratic for my
liking. It clashes with the keyword "else", which I maintain is badly
named. If it was named "next", which I maintain describes what it does much
better, then things might be different, but given the status quo, I've gone
back to doing it the old-fashioned way, with a comment.

But the point is, that's a matter of *taste*, not a matter for the compiler.
If somebody else wanted to do it my way, well, that's between them and
whoever else works on their code.

You probably wouldn't want the compiler to raise a syntax error because you
put a blank line or a comment somewhere the compiler writer disapproved
off. For example, some people insist that the first line of code must
follow immediately after the docstring, some prefer to leave a blank line:

    def spam():
        """Docs"""
        code

    def eggs():
        """Docs"""

        code


Your suggestion to raise a syntax error in the case of for...else without
break strikes me as no different from the idea that we should raise a
syntax error if there is/isn't a blank line after the docstring. (Choose
one.)



-- 
Steven




More information about the Python-list mailing list