Comment by adrian_b

1 day ago

Following the recommendations of Knuth, the language Mesa, which was implemented at Xerox during the seventies, and which was a source of inspiration for various later languages, including Modula, Ada and Python, included a form of "restricted GOTO" which is the most useful kind of GOTO in my opinion.

The Mesa restricted GOTO allowed jumping forwards, but not backwards, and it allowed jumping towards an outer block, but not towards an inner block.

These 2 restrictions eliminate all the "harmful" features of the traditional GOTO, while retaining its advantages for handling exceptional conditions or for terminating multiple levels of nested program structures.

The Common Lisp TAGBODY appears to be only partially restricted, by allowing backward jumps, so it does not prevent the kind of hard-to-understand program structures for which GOTO was criticized.

GOTOs in random directions may be used to implement state machines, but such state machines can still be implemented in a language with restricted GOTO by not using GOTO, but by using mutually recursive procedures, if tail-call optimization is guaranteed.

In Common Lisp's tagbody, labels must be immediate children of the form; they cannot be anywhere in the enclosed syntax.

The go form identifies the tagbody which is the immediate parent of the selected label. It then performs a control transfer which selects that form as the exit point; every form in between is abandoned with all the unwinding. Then that tagbody passes control to the selected label.

We can imagine every tabody to be a kind of case statement which selects the first case on entry, and subsequent cases are selected by go forms; each go says "break up to the top of the tagbody, and then go to this case".

So for instance you can't have shenanigans like two loops or conditionals buried in the same tagbody, where one jumps into the middle of the other.

Jumping out of a lambda /is/ possible, but only if the tagbody within which the lambda was captured has not yet terminated. (In other words, the lambda isn't an escapee from the tagbody it's trying to use.)

I'm not clear that jumping backwards is that tough to reason with. Notably, Knuth's algorithms do that quite commonly, right?

I do think they need to be somewhat constrained to not jump to places that need new things initialized. Which, it is truly mind blowing to know folks used to just jump straight into other functions. Mid function. Because why not.

  • Knuth's algorithms do that because they are written in assembly language.

    In assembly language you must use backwards jumps to implement loops.

    However, good assembly language programmers do not use arbitrary backwards jumps, but they use only a certain number of patterns, which correspond to the various kinds of loops that are also encountered in high-level programming languages.

    Many programming languages are somewhat incomplete, because they do not have all the kinds of loops that exist in other programming languages. When programming in an assembly language, a good programmer will not restrict the loops to only the kinds of loops that are available in C/C++, but the non-nested loops that are possible with arbitrary GOTO will not be used.

    The best practice in assembly programming is to not use explicit backwards jumps, but to define macros for different kinds of loops, then use the macros, which make the code look exactly like in a high-level programming language.

    Knuth's algorithms do not use macros, like in real assembly programming, because their purpose is to show you an actual implementation, not a higher-level abstraction.

    • I am not talking about the implementations in assembly, I'm talking about the way he lists them in prose.

      I can accept that he lists them the way he does because of familiarity to a style of assembly, but that doesn't change the fact that his prose can be easier to read than some of the alternative schemes people have used. In particular, I have found them to be far more illuminating to what is happening. With some odd challenges on finding ways to convert them to the standard control structure of modern languages.

      You don't have to make a larger explanation of the argument for the more common control structures we use today. I am largely bought off on them and I am not trying to argue for a return to "only using jumps."

  • jumping backward creates all the non-linear issues I assume

    • Fair that it can create some. But just allowing of nested loops already creates some of these. And, I know folks have tried to disallow loops, but that feels extreme.

      Again, I would point to many of Knuth's descriptions as already allowing jumps forward and backward in steps as evidence that they can be useful.

      4 replies →