News for hackers. Stuff that matters.
Re:Loop Exits and Structured Programming: Reopening the Deba... by woliveirajr on Monday June 12, 2017 @04:35PM (#14540901)
I love all those "goto" disguised as any kind of flow controls. Because goto is devil, but give it another name and another smell and, voilà!, it becomes beautiful.
Re:Loop Exits and Structured Programming: Reopening the Deba... by lmkg on Monday June 12, 2017 @05:16PM (#14541184)
Yes. Because giving it a different name addresses the problem with goto.

The problem with goto is that it is opaque. It has no structure, it has no constraints, and it communicates no intent. It says "execute the code behind this label," and gives no clue what the code is or what it does.

But if you give goto a different name that reflects how you're using it, that solves the problem! If your goto is named "break" and it occurs inside a loop, that tells you what the goto is trying to achieve. It tells you at a glance what code you expect to find when you jump, and its relationship to the other code in the program. In short, it communicates intent.

The same is true when the name is "continue" or "throw": you know where in the program that goto leads and what sort of code you expect to find when you go there. You don't need to read it to find out. And if you do read it, you have a better idea of whether it's right or not.

(Also note that criticism of goto applies a lot more to historical gotos, whose argument was an arbitrary memory address. Modern gotos aren't nearly as bad because they generally take a label rather than a number, and because their jump targets are often restricted to the immediate lexical environment.)

Re:Loop Exits and Structured Programming: Reopening the Deba... by chriswarbo on Monday June 12, 2017 @07:11PM (#14541879)
> The same is true when the name is "continue" or "throw": you know where in the program that goto leads

> Modern gotos aren't nearly as bad because they generally take a label rather than a number, and because their jump targets are often restricted to the immediate lexical environment.

That doesn't actually apply for your examples of "continue" or "throw" though. "continue" has no label, numeric or symbolic; its target is implicit from the lexical scope. "throw" has a sort of label (the type/class of the given value/exception), but its target is determined by the dynamic environment. Given a function like:

    function foo() {
      throw Bar;
    }
There is nothing in the lexical environment (i.e. the blocks within which it is written) that tells us where this will jump to. That information depends on what the stack looks like at the call site, which may be written by someone else, on the other side of the world, 20 years from now.
Re:Loop Exits and Structured Programming: Reopening the Deba... by lmkg on Monday June 12, 2017 @08:25PM (#14542173)
I apologize if I wasn't clear, I didn't mean to say that continue/throw are similar to modern gotos. Renamed gotos such as continue, break, and throw address the primary drawbacks of raw gotos. Unrelatedly, the gotos that one is likely to encounter in a modern-ish language like C or Common Lisp don't have all of those drawbacks anyways. My purpose was not to draw a comparison, merely to add some additional context for Dijkstra's original rants that may explain why the hard-line stance he took at the time makes a little less sense today.
Re:Loop Exits and Structured Programming: Reopening the Deba... by gpderetta on Tuesday June 13, 2017 @02:25AM (#14543175)
Technically the same thing is true for 'return'.
Re:Loop Exits and Structured Programming: Reopening the Deba... by chriswarbo on Tuesday June 13, 2017 @06:09AM (#14543820)
That's true, although 'return' will only pop one frame off the stack whilst 'throw' will keep going until it finds a handler. Hence 'return' has a more predictable/consistent behaviour, and less context-dependence.
Re:Loop Exits and Structured Programming: Reopening the Deba... by gpderetta on Tuesday June 13, 2017 @06:35AM (#14543887)
> although 'return' will only pop one frame off the stack

Unless it was a tail call...

Really, throw and return are quite similar, both invoke an externally provided continuation and abandon the current one.

Re:Loop Exits and Structured Programming: Reopening the Deba... by chriswarbo on Tuesday June 13, 2017 @07:26AM (#14544025)
Heh, I actually wrote something about continuations, then decided against it. I was going to say:

In a pure CPS setting, the caller's "only" responsibility is to give the right arguments (in scare quotes because getting a continuation right is usually trickier than the "plain" values usually given to functions).

In languages with functions, the caller is also responsible for handling/ignoring any return value and carrying out any subsequent computations, if the procedure returns.

In languages with exceptions, the caller is also responsible for handling/passing-on any exceptions.

Out of these three sorts of language, my current preference is for the second. It's probably fair to say that most procedures in a codebase will return a value (i.e. they're not continuations) and will not throw an exception (they're exceptional, after all). Hence it seems empirically desirable to allow `return`: it alters the semantics from a pure CPS scenario to one where we must always be vigilant that a procedure may return; but since most procedures (in such languages) return, that's not an edge case (in fact it's continuations, procedures without `return`, that are rare!); hence the benefits seem worth the cost.

When `throw` is introduced, we again must be vigilant when we're calling procedures. However, since most procedures don't `throw`, the benefit of having this ability is less than that of `return`. Also, since there may be many occurrences of `throw`, with different exception types, the cost of looking out for and handling these is greater than for `return`.

Hence my (current) opinion that `return` is very much a "good GOTO", whilst `throw` is a "bad GOTO" :)

Then we get `call-cc`... ;)

Re:Loop Exits and Structured Programming: Reopening the Deba... by gpderetta on Tuesday June 13, 2017 @10:15AM (#14544816)
The trick with throw is, of course, assuming that any procedure may potentially throw, except for a selected 'always succeeding' few. So avoid any non-local mutation and for those that are unavoidable take a transactional approach.

'call-cc', the control operator from hell that puts 'spaghetti' goto to shame.

Re:Loop Exits and Structured Programming: Reopening the Deba... by gpderetta on Tuesday June 13, 2017 @06:36AM (#14543894)
edit: but yes, of course I'm being pedantic.
Re:Loop Exits and Structured Programming: Reopening the Deba... by sigjuice on Monday June 12, 2017 @05:12PM (#14541159)
I encountered one of these beautiful things you mention not too long ago.

  void foo()
  {
      do {
          for (...) {
              if (...) {
                  break;
              }
          }
   
          if (...) {
              ...
              if (...) {
                  ...
                  break;
              }
          }
   
          /* even more nested loops and conditionals with break sprinkled all over */
   
      } while (0);
   
      ...
  }
PS: IMHO this code goes too far with the "goto considered harmful" philosophy. The "while (0)" is serving the purpose of a label. An explicit label instead would be more readable, IMHO.
Re:Loop Exits and Structured Programming: Reopening the Deba... by erikpukinskis on Monday June 12, 2017 @05:45PM (#14541381)
Smells bad, but could legitimately be the correct structure. Depends on the problem and performance constraints. If you have a lot of conditionals and it's too expensive to run them all, nested ifs are correct. If you need to break from conditions arising from a multiple indexes, then nested loops are correct.

Dogma has no place in coding. Best practices give you a sense of smell. They don't tell you how to factor.

Re:Loop Exits and Structured Programming: Reopening the Deba... by openasocket on Monday June 12, 2017 @06:49PM (#14541763)
You would never use nested if statements for performance reasons. The conditions in any modern language will short circuit, which will give the equivalent behavior as manually making nested loops. Depending on the circumstances it might be more readable, but never more performant.
Re:Loop Exits and Structured Programming: Reopening the Deba... by taneq on Monday June 12, 2017 @09:39PM (#14542388)
Nested if-statements lets you avoid re-evaluating the conditions between if-statements. Short-circuit evaluation only partially addresses that.

It can be cleaner to cache the conditions in a few flags if you want to do it that way, though. I'm not saying deeply nested conditionals are pretty, just that there are some cases where they're quicker. (Of course, if you're scraping the bottom of the barrel for that kind of speedup then chances are, there are algorithmic improvements that will yield orders of magnitude more gains.)

Re:Loop Exits and Structured Programming: Reopening the Deba... by openasocket on Monday June 12, 2017 @06:45PM (#14541739)
Depending on the specifics, an alternative is to break that "while(0)" loop into a function and use "return" instead of break.

Even more of an alternative (and would only be a good idea in certain circumstances) is, in a language with destructors, to make an object created at the top with a destructor for that last block of code, so you can throw out the loop and use return instead of break. This would also work with Golang defer statements. But again, only a good idea if that makes some sort of sense in your program, and isn't just a work around for the lack of gotos.

Gotos make me cringe, a "while(0)" hack makes me cringe just as much. The whole problem with goto is it confuses the reader as to where the control flow is going, but this is just as bad because until you read to the end you're under the impression those statements will be run multiple times.

Re:Loop Exits and Structured Programming: Reopening the Deba... by Symmetry on Monday June 12, 2017 @05:11PM (#14541154)
At a hardware level all control flow is made up of gotos. But by writing them as 'for' or 'break' or 'if' in our programs we make them easier to read by letting the reader know that the jump is restricted in certain ways.
Re:Loop Exits and Structured Programming: Reopening the Deba... by woliveirajr on Tuesday June 13, 2017 @10:49AM (#14545129)
Yes, I know :) . Having learnt to program some microprocessors, I love how everything is evaluated in terms of jump-if-zero, jump-if-not-zero. Well, even some cracks to copy-protection from older games (dating myself here) was just a single change from a hex 74 to a hex 75 somewhere in the code.

And, sometimes, even in small programs, knowing where you are after some jumps is a messy thing.

I just don't have all that rant against jumps (or goto's). I know where they live, what they eat, when they are useful and when are not. :)

Re:Loop Exits and Structured Programming: Reopening the Deba... by nickpsecurity on Monday June 12, 2017 @11:40PM (#14542724)
Nah, hardware is more like dataflow or flow-based programming. Maybe functional programming, too. Goto is jumping to arbitrary, imperative code in RAM with an address and no regard for current state. There's possibly something in hardware like it but default stuff I saw in books were more like FSM model focused on data flows.
Re:Loop Exits and Structured Programming: Reopening the Deba... by Symmetry on Tuesday June 13, 2017 @01:56PM (#14546963)
You're right that I should have said "At the level of the software as understood by the hardware" or something instead.