2018-05-22

Harmful go to (heck)

Edsger Wybe Dijkstra.jpg

My youth as a programmer began with Spectrum 48k's basic, then continued with 68k assembly on Amiga, which I preferred to Pascal (tried to learn it using the German manual of Kick Pascal), and also to C (often I “translated” C sources into assembly code…).

I think this is why I wasn't scared at all by gotos, and why I was disappointed to hear of rumors saying that gotos are evil. Now this is “common knowledge” — an ugly expression which should raise an alert flag to keep your mind more open and vigilant than usual — widely taught, and become dogma.

Torvalds' (and others') opinions in this old thread are closer to my feelings on the subject.

Excerpts (colors and emphases are mine) and comments:

I'm REALLY opposed to the use of the word "goto" […] Gotos are BAD, very very bad.

I think goto's are fine, and they are often more readable than large amounts of indentation […] Of course, in stupid languages like Pascal, where labels cannot be descriptive, goto's can be bad. But that's not the fault of the goto, that's the braindamage of the language designer.

I have always been taught, and have always believed that "goto"s are inherently evil […] Never mind the philosophical arguments, I'm just talking good coding style for a relatively small piece of code. […] if you can structure your code properly, you should never need a goto, and if you don't need a goto you shouldn't use it. It's just "common sense" as I've always been taught. Unless you're intentionally trying to write code that's harder for others to read.

No, you've been brainwashed by CS people who thought that Niklaus Wirth1 actually knew what he was talking about. He didn't. He doesn't have a frigging clue. […] Any if-statement is a goto. As are all structured loops.

An[d] sometimes structure is good. When it's good, you should use it.

And sometimes structure is bad, and gets into the way, and using a "goto" is just much clearer.

For example, it is quite common to have conditionals THAT DO NOT NEST.

Excessive nesting is code smell…

The Pascal language is a prime example of the latter problem [duplicate the code, and rewrite it in a nesting form so that you can use the structured jumps]. Because it doesn't have a "break" statement, loops in (traditional) Pascal end up often looking like total shit, because you have to add totally arbitrary logic to say "I'm done now".

And it happens in C too, if programmers want to avoid the goto because it is dogmatically evil.

What you [the guy pretending to fix the kernel code removing some gotos] do with your methods, is replacing a branch with a set, test then branch. More efficient ? I don't think so ! More readable ? I don't think so. Linus is right, you've been brainwashed :-)

As you can tell from the date, this article [Dijkstra's paper on evil gotos] predates modern programming languages and idioms; it comes from a time when Fortran ruled, and before Fortran 77 provided significant tools for avoiding spaghetti code.

It only goes to show that the core of computer science hasn't changed over the years. While some technology changes, the science itself stays the same.

This statement has its own issues, but it becomes better2:

Of course, this isn't a computer science mailing list, this is a linux kernel mailing list, so I apologize for getting off topic. Let's agree to drop it here.

But it IS a computer science mailing list.

Applied, practical, real world computer science - This is where the rubber meets the road - and interesting theories that don't cut the mustard in real life are discarded.

Ok, I cited more than needed — but it was fun. Here a non-fragmented collection of several emails — there's other interesting stuff and other opinions worth to be read, at least the one against what this guy (who thinks he's making the code better) wanted to do.

Now stop this.

The obvious uses in C

Clean up after an error. This is close to what I was used to do in assembly (of course), with “deallocation” of resources done in reverse (alloc/open resource A, alloc/open resource B, alloc/open resource C, error → free B, free A, return).

This is frowned upon too, even if it is the most obvious, clear, elegant and efficient way.

Another obvious use is when you need to exit a nested loop. I hate this or similar “patterns”:

bool cont = true;
for (int i = 0; conditionI(i) && cont; ++i) {
  for (int j = i + 1; conditionJ(j); ++j) {
    //...
    if (something()) {
      cont = false;
      break;
    }
  }
}

And it can be even worse:

bool cont = true;
for (int i = 0; conditionI(i); ++i) {
  for (int j = i + 1; conditionJ(j); ++j) {
    //...
    if (something()) {
      cont = false;
      break;
    }
  }
  if (!cont)
    break;
  other_things();
}

Here a goto would be “obvious” and feel good.

Though, named loops would be nice. The following isn't C:

outer:
for (....) {
  for (.....) {
    // ...
    if (something())
      break outer;
  }
}

break alone would break the inner loop, break LABEL would break from the loop following the label. I think this hardly will become C; anyway I think it is better than break 2 to say to break two loops…

What about Ada

The wikibook says:

One often hears that goto is evil and one should avoid using goto. But it is often overlooked that any return which is not the last statement inside a procedure or function is also an unconditional statement […]

Therefore if you have functions and procedures with more than one return statement you can just as well use goto. In practice, nearly every programmer is familiar with a 'return' statement and its associated behavior; thus, when it comes down to readability the following two samples are almost the same [code snippet follow] […]

Because the use of a goto needs the declaration of a label, the goto is in fact twice as readable than the use of return. So if readability is your concern and not a strict "don't use goto" programming rule then you should rather use goto than multiple returns.

The given example allows for a structured rewrite, but it can't be used as a strong argument against goto because this structured rewrite isn't always possible and/or as easy and readable as it is in this case.

Anyway, the syntax for a label in Ada is

<<Label>>

while this syntax:

Label:

can be used to name loops. E.g.,

with Ada.Text_IO; use Ada.Text_IO;

procedure Nested_Loop is
   I, J : Integer;
begin
   J := 2;
Outer: 
   loop
      I := 1;
   Inner:
      loop
     Put_Line (Integer'Image (I + J));
     I := I + 1;
     exit Inner when I > 40;
     Put_Line ("o " & Integer'Image (I));
     exit Outer when I + J > 50;
     Put_Line ("===");
      end loop Inner;
      J := J + 1;
   end loop Outer;
end Nested_Loop;

(This code is meaningless.)


  1. It was Dijkstra in a famous paper; this is aknowledged, and about it Torvalds says: Yeah, he did, but he's dead, and we shouldn't talk ill of the dead. So these days I can only rant about Niklaus Wirth, who took the "structured programming" thing and enforced it in his languages (Pascal and Modula-2), and thus forced his evil on untold generations of poor CS students who had to learn langauges that weren't actually useful for real work.

  2. Like this, Much like a physicist should know the outdated theories of relativity by Albert Einstein… and an answer to this: You think Relativity is outdated ?!? I can't bel[ie]ve !

No comments:

Post a Comment