
Goto in Python - wallunit
https://github.com/snoack/python-goto
======
david-given
Hey, awesome! And it produces a true goto as well, working on the bytecode
level. I wonder if there's a way to remove the extra nops?

This is really, really useful for doing language-to-language translation.
Without the ability to arbitrarily branch from basic block to basic block,
it's possible for the basic block graph produced by a program in one language
to be simply inexpressible in your target language. You end up having to fake
it with a while loop and switch statement, which has horrible performance
implications.

(Emscripten has some exotic algorithms to try and express the source program's
basic block graph using the target language's structured programming
constructs. This is vitally important to make goto-less Javascript perform
well, but it can't work in all cases, and some C functions will force
Emscripten to fall back to loop-and-switch. And, of course, these functions
are typically the ones which have been carefully hand-optimised for speed...)

~~~
wallunit
> I wonder if there's a way to remove the extra nops?

Yes, technically it would be possible to remove the code instead injecting
NOPs. But then I'd have to adjust the jump targets. However, I don't think
these NOPs are too bad. Note that goto jumps directly after the NOP ramp of
the label, and the NOPs of the goto itself are never reached. The only
scenario where NOPs are actually seen by the interpreter is when the natural
code path visits a label.

EDIT: Instead re-assembling the bytecode and adjusting jumps, I went to use
JUMP_FORWARD(3) instead 7 NOPs now. Note that there are still 4 NOPs left to
fill the gap, these however are never executed as they are skipped by the
preceding jump instruction. [https://github.com/snoack/python-
goto/commit/2b0f5e5069cbb88...](https://github.com/snoack/python-
goto/commit/2b0f5e5069cbb88776b0d070d6608e4064735d96)

~~~
masklinn
Is it certain that JUMP_FORWARD is faster than a few NOPs?

~~~
wallunit
I did run the example from the README, with "%timeit range(0, 1000)" in
ipython:

10000 loops, best of 3: 72.9 µs per loop @ CPython 2.7.10 with NOP

10000 loops, best of 3: 77.2 µs per loop @ CPython 2.7.10 with JUMP_FORWARD

10000 loops, best of 3: 106 µs per loop @ CPython 3.5.0 with NOP

10000 loops, best of 3: 106 µs per loop @ CPython 3.5.0 with JUMP_FORWARD

100000 loops, best of 3: 8.6 µs per loop @ PyPy 2.4.0 with NOP

100000 loops, best of 3: 8.7 µs per loop @ PyPy 2.4.0 with JUMP_FORWARD

To my surprise, in fact, JUMP_FORWARD isn't any faster than 7 NOPs. In Python
2.7, JUMP_FORWARD is even slower. So reverted:

[https://github.com/snoack/python-
goto/commit/d19d244a9e5efdf...](https://github.com/snoack/python-
goto/commit/d19d244a9e5efdfebda0b1be8440f881badf6f67)

Thanks for the pointer!

------
mrsirduke
The technique used to implement this is rather interesting, and the
implementation itself[1] is very understandable.

I'm sure neither should be used in a production project.

[1]: [https://github.com/snoack/python-
goto/blob/master/goto.py](https://github.com/snoack/python-
goto/blob/master/goto.py)

------
simgidacav
[https://www.xkcd.com/292/](https://www.xkcd.com/292/)

Sorry, I had to.

~~~
wallunit
Awesome, I added it to the README!

------
tveita
This is cute but of course horribly unsafe since it doesn't respect control
statements.

Some examples you'd expect to work that will crash the interpreter:

    
    
      @with_goto
      def fun1():
       while True:
        for x in [1]:
         goto .next
        label .next
      
      @with_goto
      def fun2():
       goto .next
       for x in [1]:
        label .next
      
      @with_goto
      def fun3():
       while True:
        try:
         goto .next
        finally:
         print('skipped')
        label .next

~~~
RussianCow
How would you expect the second one to work?

~~~
tveita
I guess similarly to C:

    
    
      for i in foo:
        print(42)
    

would be considered equivalent to something like

    
    
      it = iter(foo)
      while True:
       try:
        i = next(it)
       catch StopIteration:
        break
       print(42)
    

and jumping to print(42) would skip the init and first iteration expression.

Of course that's non-obvious behaviour, so in practice you would make it a
compile time error. A careful language designer might even decide to not
support goto at all. :)

------
polemic
Incidentally, from 2009: [http://code.activestate.com/recipes/576944-the-goto-
decorato...](http://code.activestate.com/recipes/576944-the-goto-decorator/)

And presented at KiwiPycon 2014:
[https://www.youtube.com/watch?v=DdU8I09BGsU](https://www.youtube.com/watch?v=DdU8I09BGsU)

------
moretti
Here's another Python 3 compatible implementation:
[https://github.com/cdjc/goto](https://github.com/cdjc/goto)

------
ssanderson11235
If you're interested in this sort of thing, you might also check out
[https://github.com/llllllllll/codetransformer](https://github.com/llllllllll/codetransformer),
which is a general-purpose library for doing these sorts of shenanigans in an
almost-sane way. (Disclaimer: I'm one of the library authors).

As an example, I've got a PR open right now that lets you do things like:

    
    
        @mutable_locals
        def f():
            out = []
            x = 1
            out.append(x)
            locals().update({'x': 2, 'y': 3})
            out.append(x)
            out.append(y)
            return out
    
        assert f() == [1, 2, 3]
    

which works using a combination of ctypes hackery and replacing all LOAD_FAST
instructions with appropriately-resolved LOAD_NAME instructions.

------
veddox
47 years after Dijkstra's "Go To Statement Considered Harmful"[1], the
Structured Programming Wars flare up again on Hacker News :D

Neat hack, though.

[1]
[http://www.u.arizona.edu/~rubinson/copyright_violations/Go_T...](http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html)

~~~
jsbg
Someone wrote an implementation of COMEFROM (a joke response to the Dijkstra
article) along with GOTO in Python for an April fools' day joke as well.

[https://en.wikipedia.org/wiki/COMEFROM](https://en.wikipedia.org/wiki/COMEFROM)

~~~
sklogic
Comefrom is a mainstream now, not a joke any more. See the phi nodes in SSA.

------
Veedrac
I was aware of the April Fools version.

The idea that it needs optimizing, no doubt because someone's using it in a
hot code, is hilarious. That's either the best satire or the worst truth I've
heard all day.

~~~
sklogic
I am totally going to use this approach in a production code. Because I am
using generated code a lot.

------
ainiriand
WHY?! Good experiment anyway, well done.

~~~
aap_
Because goto can be useful?

~~~
Walkman
You are not a Python programmer for sure :)

~~~
JupiterMoon
In e.g. fortran there are labelled loops. So one can break out of an inner
loop back to the main program flow. I know that this wouldn't be pythonic but
sometimes I just want to finish the one off script and get back to something
more fun and I miss this ability. I still don't think arbitrary gotos are
great.

~~~
k8tte
You can resolve this in basically every language by reworking your code in
smaller pieces, and simply returning from a loop. Jumping left and right
should only be signalling that you are Doing It Wrong (tm)

~~~
scott_s
See the reply by Intermernet elsewhere in this discussion:
[https://news.ycombinator.com/item?id=10251397](https://news.ycombinator.com/item?id=10251397)

------
WalterBright
goto is sometimes the right tool for the job.

~~~
Retra
Theorem: "X is sometimes the right tool for the job" is true for all X.

Now could we maybe explain what jobs we're talking about and why it's the
right tool for them?

~~~
kazinator
Goto is absolutely the right tool for: "the instruction pointer finds itself
here, and at this critical juncture in the algorithm, one would rather prefer
that it were over there, for want of correctness, if not beauty."

~~~
trentnelson
That is an excellent quote.

(Where's it from? Google-fu yielded nothing.)

------
csl
Nice. But does it handle extended jumps?

I know this is a problem for at least the Byteplay module.

