My favourite part of the Knuth paper is this:
begin until error or normal end:
if m = max then error ('symbol table full') fi;
normal end;
end;
then
error (string E) =>
print ('unrecoverable error,'; E);
normal end =>
print ('computation complete');
fi;
This is pattern-matching on a Result<(), String>, in 1974. How adorable!It’s a shame that innovation in this area has stagnated so much: most languages only feature IF, WHILE, FOR, single-level CONTINUE/BREAK, SWITCH (with fallthrough), and GOTO as an escape hatch in case the others are insufficient, just because they are so familiar. Only now languages like Python or Java are getting any form of pattern matching. Though props to the latter for at least allowing to break multiple loops by way of labelling the outermost one.
Writing corresponding nested maps/folds is generally quite unsightly. Plus the resulting code may be non-tail-recursive which means that the sentinel EarlyExit value will get checked and re-returned for every function in the call stack on such exit.
This loop construct, however, basically introduces locally scoped continuations that you can directly return to. Of course, it's possible to optimize nested maps/folds down to this form, but it requires very heavy inlining — Haskell does it because it's semantics allow deforestation, but does Rust do it?
On a side one could argue that early returns, while often used, are not structured programming and a goto in disguise.
If it were not for being autorejected in code reviews I would use goto:s alot more.
Even if I want to break out of nested for:s there is always whining about jumps.
In some cases it makes the code more readable as Knuth shows. Dogmatism is a bane of programming.