Along the lines of rewriting bytecode at runtime, https://www.python.org/dev/peps/pep-0302/ lets you arbitrarily hook Python's import process and rewrite .pyc files as desired.
As a practical application, pytest uses this to take the statement `assert foo == bar` and rewrite the AST into a number of instructions that extract the values of foo and bar and print them if the assertion fails: http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests... (https://docs.pytest.org/en/6.2.x/assert.html for those unfamiliar with the library) - so if you use pytest, you're already relying on these arcane techniques!
I do the same in my module that enables increment operators in Python by patching the bytecode for two unary plus/minus ops: https://github.com/borzunov/plusplus
Lua has `goto` since 5.2; you don't need a continue keyword. e.g.
for z=1,10 do
for y=1,10 do
for x=1,10 do
if x^2 + y^2 == z^2 then
print('found a Pythagorean triple:', x, y, z)
print('now trying next z...')
goto zcontinue
end
end
end
::zcontinue::
end
That's nicer than continue really (as someone who doesn't know and has never written Lua) - works for whichever level of nested loops like that that you want, and allows/somewhat forces you to come up with a name that might be clearer what it's for.
Java actually has an elegant-for-Java feature that removes most credible uses of goto. You can label and break out of standalone code blocks. It makes error handling without exceptions no bad.
And in most C-style languages that don’t have labelled blocks like this, you can use an infinite loop to achieve the same effect; for example, in Rust where labelled blocks aren’t yet stable:
'label: loop {
break 'label;
}
And if you want to go one further on that, you can break with value because Rust is expression-oriented:
Labeled break/continue are present in most languages that postdate Java. Go, JavaScript, Kotlin, Nim, Rust, Zig, for example, all have labeled break/continue as well. The only notable post-Java language I'm aware of that doesn't have it is C#. Languages that predate Java generally don't have such a feature--and this includes C/C++.
One thing I don't like about goto is that it allows the creation of irreducible control flow. Makes all kinds of optimizations and program analysis more difficult. When I have the chance, I advocate for more well behaved alternatives such as labeled break statements. Although they're more restrictive, most of the time they're all you need.
We are still paying this mistake as of today: most devs don't scope their async tasks lifecyle leading to a huge spaghetti code.
GOTOs are too easy to misuse.
We used to have only GOTOs, no loop, no if... But specialized keyword are better: they enforce a cleaner code, leverage conventions, play batter with tooling and make the whole experience easier to read and debug.
I recently needed to do some "what if" tax scenarios. I wrote functions for each relevant IRS form that pretty much just followed the form. For example, for Schedule D I had one line for each form line.
For example lines 15 through 20 ended up like this:
The variable numbers match the form's line numbers, and the code is a straightforward implementation of the instructions for the line.
The instructions include things like "If line X is greater than line Y, go to line Z".
I think the code would have been clearer if I could have used goto to match the instructions instead of having to convert to if blocks. For the forms I was interested in at least the if blocks were all fairly simple and the lines in the code were all in the same order as the lines on the form, but I'm not sure that would be true for all of the IRS forms.
And bonus, you can put them in several files, and import them when your program grows. Which means you can then call them in the shell to play with them.
Functions calls would have been clear enough. They are essentially just gotos with arguments when they are in tail position.
And for the tax stuff, you wouldn't even need to worry too much about having properly optimized tail calls, as the business logic here is unlikely to blow up your stack.
For what it's worth, I actually think your code is clean as is, as it clearly shows the relationships of the computation, and it's easy to understand the flow of data. I'd just suggest naming the variables as best as possible, e.g. d16_NetIncomeRaw = min(...).
The code’s logic would be able to better match the logic flow of the tax statement logic flow which often say things like “if no whatever, skip to #farther.down.the.page.”
Sometimes it’s best to let art^h^h^hcode imitate life.
For Zig, note that there's not really a use case for goto that you can't do with other language features.
To jump backwards, there's labeled continue.
To jump forwards, there's labeled break.
The use case that computed goto tries to solve is addressed with labeled continue on a switch [1]
“Proper” gotos, like continuations, are hard to reason about and optimize.
Restricted versions each covering a major use case are easier to reason about, and to optimize.
Anyhow, pretty much every version of “goto” using the name implemented or proposed for Python or other structured languages is castrated: typically, it take only a static label and has restrictions on where it can jump based on structure (out of blocks but not into blocks, within the same function, is pretty typical.)
Proper goto can jump anywhere in the program, even dynamically computed at runtime, and if state isn't set up the way the code jumped to expects, too bad. Everything more restricted is just dickering about how much it should be castrated.
That was even the whole point behind Dijkstra's paper. To paraphrase, "Goto is a very powerful language feature. Which is exactly the problem. Less powerful features give programmers less latitude to do clever things their colleagues (and the compiler) can't reason about effectively."
Ye sure. I meant that it feels like jumping through hoops to introduce a lot of different mechanisms just to remove goto:s. E.g. "labeled breaks" and using silly loop constructs.
These features already have reasons to exist, because they solve control flow scenarios. So there they are. And then we are brought the question of introducing goto, and there's just nothing that it adds. It would make the language more complicated for no reason.
They're not set up in the most elegant way, but the concept of leaving a nested loop is very simple and should definitely be possible without a goto. And switching between states in a state machine, where each state has a single entrance like a normal function, should also be simple.
With goto you can't know at a glance if the code is following those patterns or doing some kind of spaghetti nonsense. It hurts readability.
Wow I'm actually pretty blown away! I use Go on a daily basis and have never seen it used anywhere or had any clue it was in the language.
If you had asked me a few minutes ago if Go had goto I would have said "definitely not and they would never add it because it goes against the Go ethos".
I feel like they usually prioritize simplicity and readability instead of tricks, syntactic sugar, and one-liners.
For example there's no ternary operator. Instead you'll have to write out a few lines of if/else. There's no function overloading or operator overloading, no built in min/max, and currently no generics (though they're being added).
They definitely try to keep the number of keywords down to a minimum compared to languages like Swift where you could write the same business logic in fewer lines using stuff like try, guard, !, and ?.
Wouldn’t have been very useful, there were not that many incompatible syntactic changes you could not ignore or paper over (string literals were the main issue until u”” was restored) and it would not have helped any with the semantic & api changes.
Don't know if I agree about the goto thing, but there are actually a number of options now for delivering varying degrees of self-contained Python executable.
When I evaluated the landscape a few years ago, I settled on PEX [1] as the solution that happened to fit my use-case the best— it uses a system-provided Python + stdlib, but otherwise brings everything (including compiled modules) with it in a self-extracting executable. Other popular options include pyinstaller and cx_freeze, which have different tradeoffs as far as size, speed, convenience, etc.
Oh sure, a statically-compiled language is always going to win on this for just having it out of the box.
But that can also be seductive in its own way, like when you get used to just deploying a single binary but then suddenly have an application that also includes data files— do you do the go:embed thing to maintain the glorious single binary, or do the pieces go in an archive together? If it's that one, do you find the data at runtime with a flag, or an envvar, or a relative path, or something else?
Undoubtedly, Go is easier than Python or Ruby, especially for the trivial case. But deployment is overall a non-trivial problem.
This sounds like a case of perfect being the enemy of good. Yes, no deployment system will cover all cases. But that doesn't mean it's impossible to make a system that's good for most cases.
Just... out of curiosity as someone who learned to code in BASIC on a TRS-80... WHY ON EARTH would you ever want goto or gosub statements instead of proper loops and methods? It's not like they're faster. They certainly don't make code more readable. The last time I had to use that type of construct was gotoAndStop() in Flash, which at least had the benefit of queueing up a bunch of art and sound effects. Other than the "neat-o" factor, what's the use case or impetus behind this apparent horror?
I had to use GOTO label, once so far, as a way to quickly exit from inside nested loops - you can do it without GOTO but the point was to be efficient(like if you work with the pixel colors of a big image , each extra instruction inside the loops will have a visible time effect).
In a situation like that I would usually define a boolean before entering the top loop, and set that true within once a condition is met, causing all the outer loops to break if that condition is true. I can't imagine there would be a major speed difference in some other way of breaking all the loops to jump to a different line of code.
That means once it’s set to true, all the inner loops would need to at least finish that iteration. That could be a huge speed difference at a minimum or a completely wrong implementation.
this means you do a if check each loop, this is bad if you do some say image filter/effect where you work with big loops. end you get some nested if and break inside the code, is much more readable to just jump out.
Pythons for-loop is not working with boolean, so using one would mean to uglify the code. A goto would simplify the workflow by removing 1-2 lines for each additional loop.
probably faster since it was built for this purpose, throwing an error would have to at least create an exception object, then create stack trace and the other setup stuff.
As i said I code for almost 20 years and I only had to use it once so far, but people that maybe work a lot more with stuff that needs a lot of performance might use it much more often.
First of all, you wouldn't want it. I am an old programmer, so I still remember being taught to code before structured programming became universal. And I can personally attest the structured programming was a great advance.
In case you haven't heard the term, structured programming is the term that was used for replacing goto with a handful of commonly used control flow structures. A loop, a function with a return statement, and some kind of case statement (or multi branch if) were the basic ones.
If you look at modern languages today almost all of them have the same set of control flow constructs. They have an if statement, a loop, some kind of function with a return statement, most have some kind of case or multi-branch if, and must have some sort of exceptions. (If was present before structured programming; exceptions were added later.)
If you look at the very oldest programming languages they may have some of these control flow structures but they definitely have two: the if statement and the goto. Because the goto statement is strictly more powerful than these other control structures. If you have a language that doesn't support exceptions but does have a goto, then you can write an exception-like flow using goto. If you have a language that doesn't have break or continue for its loops you can build a loop that works that way using goto. If you're programming language doesn't have the broken form of case statement (that falls through to the next branch) so you can't build Duff's device [https://en.m.wikipedia.org/wiki/Duff%27s_device], then you can build that using goto.
In fact, the combination of if and goto is powerful enough to create any possible (single threaded) flow control structure including ones you and I have never even thought of.
The reason that structured programming was such an important and powerful step forward is because it made the language strictly less powerful. Because the language was less powerful, it was possible to reason more clearly about what the code could or couldn't do. In particular, it allowed a programmer to make certain assumptions about locality. If you started reading at the top of a function and worked your way down you could reason about what this function did in isolation: perhaps the tangle of loops and ifs and try-except blocks in the function are confusing, but at least you don't have to read the entire rest of the program to be certain what it is doing. With goto in your language that guarantee is gone: you might have to understand the entire rest of the program in order to figure out any one function.
So, we gave up the power to create certain flow control constructs in order to make it possible to reason about our languages. But there is nothing that says that the evolution of programming languages is finished. Exceptions are pretty nifty, and they were not part of the original toolkit. Maybe there is another control flow structure out there waiting to be discovered which is extremely useful for solving certain kinds of problems. Someone with goto in their language could discover and use that control flow structure, while those of us without goto cannot.
> With goto in your language that guarantee is gone: you might have to understand the entire rest of the program in order to figure out any one function.
The same is true, to a greater degree, for function calls.
I guess that depends on if the language supports tail recursion and/or you're 100% sure it won't blow the stack. If you need to retry forever, without using any memory to do so, goto is perfect.
Exceptions are forward gotos. A "while" loop with continue and break is backward goto.
The only thing missing is break from nested while. Java has one, called break with label, one of more obscure features of language.
So, in a sense, python already does have goto.
Great! It's too bad that python's jump instructions only accept constant targets or constant offests (which seem like they end up being equivalent), otherwise this approach could also support labels-as-values.
As a practical application, pytest uses this to take the statement `assert foo == bar` and rewrite the AST into a number of instructions that extract the values of foo and bar and print them if the assertion fails: http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests... (https://docs.pytest.org/en/6.2.x/assert.html for those unfamiliar with the library) - so if you use pytest, you're already relying on these arcane techniques!