Python-list Digest, Vol 169, Issue 23

Steve D'Aprano steve+python at pearwood.info
Fri Oct 13 12:45:06 EDT 2017


Hi Roger,

Unfortunately you seem to have messed up the quoting (*and* the subject line,
and the threading) for this post. The perils of replying to digests.

I'll try to repair the worst of the misquoted text as I go, but if I
accidentally attribute something I said to you, well, that's only because you
did so first :-)


On Fri, 13 Oct 2017 02:53 am, ROGER GRAYDON CHRISTMAN wrote:

> On Thu, Oct 12, 2017 08:05 AM, Steve D'Aprano wrote:>

[...]

> This seems like a veritable straw man if any.
> I am fairly sure that "one entry, one exit"
> does not precisely mean "no branching whatsoever"
> Nor does discouraging unrestricted GOTO
> suggest that either.


But Meyer doesn't just prohibit unrestricted GOTO. He prohibits, quote:

    'any "break" or similar control mechanism.'

Similar control mechanisms include *anything that branches*: they all have in
common the fact that they branch, and that they jump from one executable
statement to another, potentially far distant statement, rather than executing
the next statement unconditionally.

Now, you would be right that I was straw-manning *if* I had argued that:

    "Meyer is wrong because he wishes to prohibit `if` and `for` 
    and function calls."

But that is not even close to what I said: paraphrasing slightly, I said that
if we take Meyer literally, that is what would be prohibited, but surely
Meyer does not intend us to take him literally. That would be unreasonable.
Therefore we must infer that Meyer breaks up branching control mechanisms
into at least two groups:

- those which are allowed, such as (we must presume) function calls,
  exceptions, loops, `if...else`;

- those which are not allowed, such as `break`.

The only concrete example of a bad form of jump that he gives is `break`, so
we are left to guess at where he draws the line. I made a conservative,
reasonable guess that he would want to allow subroutine calls, looping and
conditional jumps (`if...else`).

So what I did was the *literal opposite* of a straw man: I took a statement
which is, on the face of it, obviously wrong, and strengthened it to something
much more reasonable and defensible.

Setting aside the (likely?) possibility that Meyer clarified his intention in
the book, if we judge his position on his quoted words alone, then we must
conclude that he wishes to prohibit any branching operation:

- he doesn't refer to "unrestricted goto", but to unqualified "goto";

- the one concrete example he gives, "break", is an extremely restricted
  form of goto: you can only use it to jump from inside a loop to the
  first instruction immediately outside of the loop.

As jumps go, you can't get much more restricted than "break", and yet Meyer is
still against it. Judging Meyer's *actual* words, rather than putting words
into his mouth, his position is more unreasonable and even weaker than the
one I argue against.

Hence, the very opposite of a straw man: I am arguing against an iron man, a
position stronger than the one Meyer expressed.

(One of my pet peeves is people misusing "straw man", but I must say this is
the most blatantly wrong example I've seen.)

My argument is that `break` and `continue` do not belong in the "bad list" of
jumps that should be prohibited, not that Meyer thinks that we shouldn't use
subroutines. See further below.

Ironically, the one concrete example (that Stefan quotes) of what Meyer
considers to be an allowed jump is exception handling, which many people
discourage because of its similarity to GOTO or COMEFROM:

    "I consider exceptions to be no better than “goto’s”, considered 
    harmful since the 1960s, in that they create an abrupt jump 
    from one point of code to another."

    https://www.joelonsoftware.com/2003/10/13/13/

    "The core problem is the hidden control-flow possibility. [...]
    Exception handling introduces precisely this kind of hidden
    control flow possibility, at nearly every significant line
    of code"

    http://www.lighterra.com/papers/exceptionsharmful/

    "Goto statements went out of style in the 60s, relegated today
    to be the prototypical example of bad coding. Yet hardly
    anyone seems to be bothered by exceptions, which do basically
    the same thing."

    https://petercai.com/the-case-against-exceptions/

Raymond Chen has written on why exceptions are hard to get right:

https://blogs.msdn.microsoft.com/oldnewthing/20040422-00/?p=39683/

https://blogs.msdn.microsoft.com/oldnewthing/20050114-00/?p=36693/

and of course when Rob Pike created Go, he famously left exceptions out of the
language, almost uniquely for a 21st century language.

So the irony here is that Meyer wishes to prohibit the *least harmful* and
*most defensible* form of restricted jump outside of the if/while/for
statements, namely the "break" statement; while he would allow one of the
more confusing, heavily-criticised, least-restricted jumps still allowed in
modern languages (exceptions).

In a language with exceptions, every method, function and loop can have
multiple exits. Meyer's acceptance of exceptions is in direct contradiction
to his stand that code blocks should have a single exit.

Personally, I think Meyer's position here is incoherent. It is like he wishes
to prohibit Nerf guns as too dangerous, while permitting semi-automatic
assault rifles.



> Clearly a while or for loop without break i
> is single-entry and single exit.

Indeed it is, but you have missed that Meyer made two points:

- avoid jumps;

- code blocks should have a single entry and a single exit;

and that I was criticising them both. (Or at least, criticising the strong
position that Meyer takes on each of them -- I would agree with a weaker,
more nuanced position.)

The two points are orthogonal. The first is necessary, otherwise one might
jump around inside a single block, turning it into spaghetti code, while
still having a single entry and single exit:

    for i in range(100):
        do_something()
        GOTO 20
        10:
        do_more()
        20:
        something_else()
        if condition: GOTO 10
        yet_more()
        if flag: GOTO 20
        # and if we reach here, we jump back to the start of the loop


Both Meyer and I would agree this is bad code. But he considers `break` to be
just as bad as the use of GOTO, while I don't.


I wrote:

> # with break
> for i in range(2**64):
>     if isprime(i):
>         print(i, "is prime")
>         break
> 
> # without break
> still_searching = True
> for i in range(2**64):
>     if still_searching and isprime(i):
>         print(i, "is prime")
>         still_searching = False
> 
> # without break, attempt number 2
> still_searching = True
> i = 0
> while still_searching and i < 2**64:
>     if isprime(i):
>         print(i, "is prime")
>         still_searching = False

to which you (Roger) asked:

> Do you really believe that there are no prime numbers
> less than 2**64?

Of course not.

I had hoped that these examples would be interpreted as illustrative of the
nature of the boilerplate one must often write, rather than the literal code
I would use to find the smallest prime (which, for the record, is 2). I
thought that by so clearly making the code so ludicrous, people would focus
on the structure of the code snippet rather than the fact I was looking for
primes.

In hindsight, that was a mistake, and I apologise for my lack of clarity.

I should have written something more generic:


for attempt in range(number_of_attempts):
    preprocess()
    if condition():
       print("success")
       break
    postprocess()


which then can be naively written as:


still_trying = True
for attempt in range(number_of_attempts):
    if still_trying:
        preprocess()
        if condition():
           print("success")
           still_trying = False
        if still_trying:
            postprocess()


Please don't try to tell me that nobody would write code that naive, because I
have done exactly that. And for exactly the reason Meyer gives: to ensure
that the for loop has precisely one entry and one exit. That was a long time
ago, when I was young and foolish and more prone to treat programming
principles as absolute, universal truths.

(By the way: Donald Knuth is, or at least was, one dissenter to the GOTO
Considered Harmful movement. In 1974, Knuth argued that GOTO can sometimes
lead to better code, and suggested a looser structural constraint: the
program's flow chart should be able to be drawn with all forward jumps on one
side, all all backward jumps on the other, and no jumps crossing each other.)



[...]
> That is the biggest objection programming teachers
> like me dislike the break statement -- it leads to very
> careless programming.

That's a very strong statement. Got any studies to back it up?

We (the collective programming community) have decades of experience with
GOTO, and how *in practice* it leads to spaghetti code. I don't believe that
there is any similar experience proving that "break considered harmful".

Structured programming conclusively and definitively won the structured versus
unstructured programming debate in the 70s and 80s, and consequently, hardly
any high-level languages still offer GOTO, and even low-level languages
discourage it, and put restrictions on where you can jump into and from.

Nevertheless, modern high-level languages do allow many deviations from the
structured paradigm, with "break" one of the most common. Wikipedia has a
good overview of the more common deviations:

https://en.wikipedia.org/wiki/Structured_programming#Common_deviations

Peter Ritchie suggests applying Single Exit to OOP is cargo-cult programming:

http://blog.peterritchie.com/Single-Entry-2C-Single-Exit-2C-Should-It-Still-Be-Applicable-In-Object-oriented-Languages-3F/

(although I wouldn't go quite that far).


> Once you imagine that you will 
> use a break to exit your loops, you might stop thinking
> about what your loop conditions are.  And then you find
> yourself in a corner where the only way out is to break.

It is arguable whether the existence of break encourages that sort of poor
code, or whether it causes people to stop thinking about their loop
conditions.

In any case, even if I accept that break/continue are less than ideal for
students, that hardly means that that experienced programmers should avoid
them.


[...]
> The code that generated the exception, of course, seemingly
> has more than one exit, but exceptions are just that --
> exceptions.   I hope you don't start using counting loops
> up to 2**64 to visit a 100 element array and rely on
> IndexError to exit such a loop.

That is exactly how looping was implemented in early Python (before version
2.2) and it is still available as a fall-back. Of course this was done in the
virtual machine, so it was much more efficient than if it were written
directly in pure Python code.

http://www.effbot.org/zone/python-for-statement.htm




-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list