GOTO - Love it or hate it?

BlitzPlus Forums/BlitzPlus Programming/GOTO - Love it or hate it?

Imperium(Posted 2013) [#1]
Personally I like using "Go To" but based upon the various sites and blogs I've read about Blitz there seems to be a big divide here.

Some will say that isn't very practical to use Gotos, while others say it's very fast and useful. Goto could be thought of as a one way teleporter. This could be good or bad depending on how its used.

That being said is it poor to code a game with many goto statements or gosubs? Functions are much more powerful because you can replace Blitz functions with your own. I've read that Functions can be considered little programs within a program.

Should a programmer avoid using Goto? I think it's okay to use in a small simple program, but in larger more complex project it would not be wise. Agree or disagree?


Floyd(Posted 2013) [#2]
Goto isn't always bad, just almost always.

The only good example most people can think of is breaking out of some complicated set of nested conditions. You can use Goto as a quick way to skip to the end of this section of code.

If you do use it then do so sparingly and always to move down the code. If you start branching backward and forward you soon get the classic, utterly incomprehensible "spaghetti code".


Yasha(Posted 2013) [#3]
Functions are much more powerful because you can replace Blitz functions with your own. I've read that Functions can be considered little programs within a program.

Should a programmer avoid using Goto? I think it's okay to use in a small simple program, but in larger more complex project it would not be wise.


I have several points I'd like to make here:

1) If you don't understand functions to the point where you're using them fluently and without thinking, drop everything and go and learn them. Now. Stop reading this post and pick the rest up later. If you don't know functions, you can't program: functions are the fundamental unit of program logic (from a design point of view, Goto can only exist in terms of functions; variables have no meaning without functions). Goto was historically a substitute for functions in archaic dialects of BASIC that couldn't support them properly. Functions are the "real thing".

2) There is literally no reason to ever use Gosub. Pretend it doesn't exist. It has no advantages whatsoever. Gosub in particular exists in Blitz solely to help coders coming in from older dialects to port their old code. Using Gosub is the fast way to creating unreadable rubbish: it's worse than Goto.

3) The main time one would use Goto is in generated code that you can formally prove will be correct because it's been created by machine. In small programs you will never need it, because small programs have simple enough logic to navigate properly with the normal control structures. So if you find yourself thinking "this is small, it's OK" - you're doing it backwards. Goto is an "escape hatch" for ultra-complex logic that you probably shouldn't be hand-crafting. (In the same way that Goto is a machine code operator: the CPU uses Goto a lot, but we don't need to think about it when writing loop syntax and it may as well not exist for design purposes.)

4) There is one common control operator "continue" that exists in many languages but not Blitz Basic (it got put into BlitzMax). You'll see Goto being used occasionally to fill this niche in ported code. A lot of people think this doesn't "count" since it's always used in a very rigorous way to replace a specific kind of controlled jump, and the source they're copying from has proven it's safe by using the built-in version. Use discretion here. (Of note: "continue" is nice but never essential, you can always restructure the loop to not use it, so for my part I don't do this.)

4.5) Similarly some people (notable example is the Linux kernel) use it to imitate "catch" blocks for cleaning up after error conditions at the end of a complex block of code instead of handling errors in the middle (similar to Floyd's example). Again, this is patching over an idea inherited from other languages, and should not occur naturally in idiomatic, normally-designed Blitz code.

5) There really is no divide on this issue. The controversy was back in the 70s and the pro-Goto side lost hard. No competent programmer will ever recommend the use of Goto in hand-written code for anything but patching missing features of the language, and when someone does recommend it, that's a universally-recognised warning sign that they don't know what they're talking about.

6) You don't need it. The basic control structures - and more importantly, functions - are enough to express any possible logic flow you could want. You will become a better coder if you embrace and learn to use them rather than relying on a crutch from the old world. Eventually, you'll stop thinking of how to make up for Goto and instead start just writing in terms of the new constructs. Most modern programming languages don't even include it: BlitzMax removed it; the only mainstream language that has it that I can think of offhand is C, and that's ancient.

7) Goto's efficiency is a red herring. The hoops you have to jump through to actually get any kind of meaningful control over it will completely rob your code of efficiency and clarity. The built-in control structures already emit the most efficient code to do the things that they do..!


TL;DR: Avoid it like the plague. The answer for newcomers is a firm "no". It's not OK.

Advanced users may be able to come up with corner cases (e.g. the aforementioned machine-generated source code, or hacks around features from other languages: are you seriously doing such things?), but that's getting into the territory of where rules are already breaking down anyway. Until you're able to prove to yourself (in the formal sense!) that you really need it because you've been over all of the other options, you don't need it.

In the meantime, please please please learn to use functions and you'll quickly forget this whole discussion. Functions are infinitely more powerful, and are the logical basis for 100% of programming constructs.


I apologise if this comes over as harsh (it is harsh). But the truth is that this is an old, old, resolved debate with one correct answer that you really need to accept and move on from in order to develop as a programmer, and I want to make this point crystal clear.


misth(Posted 2013) [#4]
Lol. I think Yasha covered up everything there. So yeah, I hate it. It's sometimes useful as Yasha said, but... Functions man!


Imperium(Posted 2013) [#5]
Pretty much everything's crystal clear thanks to Yasha. :)

A true Blitz scholar! I found nothing harsh about your response above. Question asked and very well answered.


Dan(Posted 2013) [#6]
The main reason why not use Goto is that nowadays programming languages
arent bound to the line numbers.
back in the days you have had to type line number infront of the text to write a program see commodore 64 and some earlier msdos basic languages.
This and the functions makes the "goto" obsolete.

The only reason why you could use goto/gosub is when you arent used to make
variables global at the beginning of your code.
this is where goto or gosub has an advantage over function.


*(Posted 2013) [#7]
Use functions instead of 'goto' the main problem here is twofold:
1) it makes code very hard to follow
2) if you decide to move on functions are the way to go so you might as well get acquainted now.


Hotshot2005(Posted 2013) [#8]
GOTO Suck and Avoid it!

Used Functions instead!


virtlands(Posted 2013) [#9]
Ahem, we need Goto for creating function pointers and other pointers.

; FastPointer library demonstration:
; (c) 2008-2009 created by MixailV aka Monster^Sage
; [monster-sage@...] http://www.fastlibs.com

Graphics 800,600,32,2
SetFont LoadFont("Tahoma",15)

FuncPtr1 = FunctionPointer()
Goto skip1
func1()
.skip1

FuncPtr2 = FunctionPointer()
Goto skip2
func2(0)
.skip2

Print " First function pointer: $"+Hex(FuncPtr1)
Print " Second function pointer: $"+Hex(FuncPtr2)
Print

CallFunction (FuncPtr1)
CallFunctionVarInt (FuncPtr2,12345)

WaitKey(): End

Function func1 ()
Print " We're in Func1"
End Function

Function func2 (v)
Print " We're in Func2"
Print " v = "+v
End Function


You can also use GOTOs to make code unreadable, (on purpose).


GfK(Posted 2013) [#10]
Use functions instead of 'goto' the main problem here is twofold:
1) it makes code very hard to follow
2) if you decide to move on functions are the way to go so you might as well get acquainted now.

GOTO Suck and Avoid it!

Used Functions instead!
Goto isn't an alternative to functions - it never has been. Gosub/Return is the nearest alternative to that, but you don't want to be doing that either - I once saw an entire game written with Gosub/Return instead of functions (Idigicon's "Derby Day", written by some guy from Lancashire if memory serves). What Goto does, is jump immediately to another section of code without adding anything to the call stack. If you use Goto excessively it will be next to impossible to debug your code, as you'll have to manually back-pedal to figure out where the code execution came from.

As has already been said, Goto has one single, practical use, and that is for jumping out of complex nested loops. Sure, there are other ways but Goto is the quickest and cleanest way and the end-user isn't going to know or care whether you used Goto or not. Never used pointers as above so can't comment on whether that's useful or not.

The main reason why not use Goto is that nowadays programming languages arent bound to the line numbers.
That's got nothing to do with it. In Blitzmax, Goto allows you to jump to a specific point in your code via a marked program label.


_PJ_(Posted 2013) [#11]
and that is for jumping out of complex nested loops.

I don't even see this as any justification for using Goto either to be honest.
Not only because once again, it makes a jump in program without any means to identify where the jump originated from, for debugging purposes - and (where Blitz is concerned at least) adding labels is ionly possible within the scope of the 'main program' - so there shouldn't* be huge nested loops in a main program in my opinion.

*In a best-practice / cleaner code way I mean.

If loops are nested in a complex fashion, I would even go so far as to suggest this hasn't been programmed as well as could be, and the use of better organisation,. "Exit" and maybe some localised checking variables could make things much simpler.


xlsior(Posted 2013) [#12]
For anything more complicated than a batch script (.bat file), goto seems to be a bad idea.


GfK(Posted 2013) [#13]
and (where Blitz is concerned at least) adding labels is ionly (sic) possible within the scope of the 'main program'
Sorry, I have no idea what you mean by that.

If loops are nested in a complex fashion, I would even go so far as to suggest this hasn't been programmed as well as could be, and the use of better organisation,. "Exit" and maybe some localised checking variables could make things much simpler.
Dunno, maybe you've just never coded anything with any degree of complexity? I have, and if I can spend half an hour fannying about with local vars to get out of multiple loops, versus three seconds of typing "Goto label", it's a no-brainer.

Time is money, and all that.

[edit] Oh, and for the record, just did a quick check and I've used Goto twice in my last three games (and both are in Crime Solitaire).


Yasha(Posted 2013) [#14]
adding labels is ionly possible within the scope of the 'main program'


This is actually false, you can place labels in functions if you like. However, Blitz guards the use of labels in a strict way that it doesn't with variables, and won't let you jump into different scopes (e.g. out of a function), nor will it let you use Gosub once functions have been activated because they interfere with each other, so you might have run into sme illegal constructs. It is certainly possible to have locally-named labels within functions for use in exiting loops.

@Gfk, I believe the suggestion might be something along the lines that one shouldn't get into the deeply-nested structures situation in the first place? Once you have them, you're right in that recreating a Goto ith state flags is even worse as not only is it the same spaghetti code, but now it doesn't even have the decency to look like what it is.

For my own part I try to keep my data separated so that in general if we're going to loop, we loop; better to avoid building structures deeper than two levels or so in the first place.

This may not always be possible though and there's a reason why other languages have brought in Continue and even named-continue.


virtlands(Posted 2013) [#15]
What Yasha pointed out is apparently true. "Blitz guards the use of labels."
It would be neat if we can do some of those strange GOTO jumps anyway.


virtlands(Posted 2013) [#16]
Following is some very unusual code demonstrating GOTO {GotoPointer) jumps.

Requites the free FastPointer Lib: http://www.fastlibs.com/

;; Some very weird code to jump from the middle of one function to another

Global LabelP[10] ;; Can be a nice collection of label (GOTO) pointers
Global CodeLIMIT = 0 ;; Just a safety variable, just in case...

Graphics 800,600,32,2
Print

LabelP[4] = LabelPointer() ;; Find the pointer of Label_4
Goto skip1
Goto Label_4
.skip1


func1(0) ;; First, let's get those internal label pointer values
func2(0)
func3(0)

func1(2) ;; Jumps to the LapelP[2] which is located in func2()

GotoPointer LabelP[3] ;; Jump from this scope to the internal of func3()

.Label_4
Print" We have returned from the inside of func3() "
Print

For z=1 To 4
Print" LabelP["+z+"] = "+LabelP[z]
Next

Print
Print" GotoPointer success. "
WaitKey():End

;; The value in "v" shall make this function behave differently.
;; when v=0, then return the label pointer to FuncLabel1
;; when v= 1...10, then things may become very very strange....
;;
Function func1(v=0)

If v=0
LabelP[1] = LabelPointer()
Goto skip1
Goto FuncLabel_1
.skip1
Return
End If

.FuncLabel_1
Print" You have arrived at FuncLabel_1"
Print" parameter v = "+v:Print

If CodeLIMIT =5
Return
Else
CodeLIMIT = CODELIMIT +1
End If

If LabelP[v]<>0
GotoPointer LabelP[v] ;; AHA! Now we can jump almost anywhere.
End If

Print" This statement shall never show. "
End Function

Function func2(w=0)
If w=0
LabelP[2] = LabelPointer()
Goto skip1
Goto FuncLabel_2
.skip1
Return
End If

.FuncLabel_2
Print" You have arrived at FuncLabel_2"
Print" parameter v = " +v
Print" parameter w = "+w:Print

End Function

Function func3(x=0)
If x=0
LabelP[3] = LabelPointer()
Goto skip1
Goto FuncLabel_3
.skip1
Return
End If

.FuncLabel_3
Print" You have arrived at FuncLabel_3"
Print" parameter v = "+v
Print" parameter w = "+w
Print" parameter x = "+x
Print

;; If this next line is commented out, then program ends prematurely here.
GotoPointer LabelP[4]

End Function



Wings(Posted 2013) [#17]
Goto was a fine of mine at comodore 64.

bye bye goto and with it stack overflows memmeory leaks.


RemiD(Posted 2013) [#18]

As has already been said, Goto has one single, practical use, and that is for jumping out of complex nested loops. Sure, there are other ways but Goto is the quickest and cleanest way


Yes i do the same.

I won't say it is the best way to do it, to be honest i don't know any other way.



2 cases where i use Goto :

If i want to exit a routine fast when a value has been found :
For i% = 1 to ThingsCount
 If(ThingLife(i) > 100)
  SelectedId% = i
  Goto LineEndRoutine
 Endif
Next
.LineEndRoutine




If i want to generate a random but controlled parameter of a mesh/pivot :
TPivot = CreatePivot()
For I% = 1 to 100
 TKind% = Rand(1,3)
 TPitch# = Rnd(-10,10)
 TYaw# = Rnd(-180,180)
 TRoll# = 0
 ChooseTreePositionTries% = 0
 .LineChooseTreePosition
 ChooseTreePositionTries = ChooseTreePositionTries + 1
 TX# = Rnd(0,100)
 TZ# = Rnd(0,100)
 TY# = TerrainY(TerrainB3D,TX,TZ)
 PositionEntity(TPivot,TX,TY,TZ)
 For OI% = 1 to TreesCount
  If(EntityDistance(TPivot,TreeMesh(OI)) < 3)
   If(ChooseTreePositionTries < 100)
    Goto LineChooseTreePosition
   Elseif(ChooseTreePositionTries => 100)
    Goto LineEndRoutineCreateTrees
   Endif
  Endif
 Next
 TreesCount = TreesCount + 1
 TI% = TreesCount
 If(TKind = 1)
  TreeMesh(TI) = CopyEntity(Tree1XMesh)
 Elseif(TKind = 2)
  TreeMesh(TI) = CopyEntity(Tree2XMesh)
 Elseif(TKind = 3)
  TreeMesh(TI) = CopyEntity(Tree3XMesh)
 Endif
 PositionEntity(TreeMesh(TI),TX,TY,TZ)
 RotateEntity(TreeMesh(TI),TPitch,TYaw,TRoll)
Next
.LineEndRoutineCreateTrees
FreeEntity(TPivot)


I don't see how it can be confusing if the "Goto Line" has a descriptive enough name. I have never had any problem using Goto.

What is the alternative ? To type more code with more functions ?
I am curious, how would you achieve the same logic than what is done in the 2 codes i have shown ?

Thanks,


Matty(Posted 2013) [#19]
There are much easier ways of doing that than using goto...goto has its place but your example of placing trees can be done with a simple while loop....


Yasha(Posted 2013) [#20]
1. The first example is a completely invalid case. Use 'Exit' to leave a loop. Goto is contributing literally nothing to this example.

2. There is usually no conceptual reason why search code and action code should appear in the same function. So the first example would be better served if the search code and the result-processing were factored out into different functions and composed at the point of use. By putting these two unrelated tasks in the same function and asking how Goto can unravel it, you're not just looking for the wrong answer but asking the wrong question.

3. Your second example is incredibly confusing and, with all appropriate respect, that is the exact kind of tangled code people are suggesting you could avoid writing if you stopped using Goto. That isn't an example of "Goto done right", it's a solid argument against using it. That code is the legendary spaghetti!

And yes in the case of no. 2 the answer is again to factor the code out into functions each of which deals with one coherent logical unit of the task (the very fact that you're using labels to simulate multiple-use of code is proof of this: you've re-invented functions in a very messy way). Have a single base loop that iterates over the entire group of trees to place; have it call out to another function to get the randomised position data with any other internal loops hidden inside that other function because knowing how they work is irrelevant to the operation of the base.

General tips:

- put each task in its own function
- when a function's task is visibly dividing into two separate tasks, split it (e.g. Your find-and-process example should either be a find function that calls a process function then immediately returns, or a generic find function whose caller composes it with a process function).
- when loop structures are more than two tabs deep, split the deeper parts out into sub functions
- there is no excuse for ever interfering with the number of iterations of a loop by manually creating an inner loop that Gotos around the outer... Just ugh, how did you even come up with something so complicated for such a simple problem?
- when a function is more than 75% of one screen long, refactor it into multiple functions that fit
- always share common code between two sites by factoring it out into a shared function
- don't be afraid to write lots of functions. There's no reason not to use them!
- functions are fast. Do not worry about performance.


So, the final answers are:
- your example code is a great example of just how bad Goto-driven code can be. It is a perfect example of the sort of mess you want to avoid
- the correct solution is indeed to write more functions. There is no reason why doing this would be a problem.


RemiD(Posted 2013) [#21]

Just ugh, how did you even come up with something so complicated for such a simple problem?


By translating instructions written in words into code :
;Create a temporary pivot
;Try to create up to 100 trees
 ;Choose the kind of the tree
 ;Define the orientation of the tree
 ;Define the position of the tree (do 100 tries maximum)
 ;Position the pivot at the defined position
 ;Check if there is at least 3 meters between the pivot and the others existing trees
 ;If yes
  ;continue
 ;ElseIf no
  ;Check how many tries have been done
  ;If tries < 100
   ;Define another position
  ;Elseif tries => 100
   ;End the routine
 ;Create the tree with the defined parameters
 ;Delete the temporary pivot


So if i understand correctly, the alternative would be to create many functions and use If Elseif Endif to run only one task at a time ?
Why not. But i don't think it is simpler to code it this way.

Thanks for the advices i will reread them later.


GfK(Posted 2013) [#22]
bye bye goto and with it stack overflows memmeory leaks.
Goto does not go anywhere near the stack buffer, so cannot cause a stack overflow OR a memory leak. If you have either, your code has other problems.


Wiebo(Posted 2013) [#23]
I have not had the need to use Goto since I stopped using procedural languages.


dna(Posted 2013) [#24]
I use the goto command. It still has its uses.


Omnicode(Posted 2013) [#25]
I'm gonna be blunt here, I honestly have only used the GOTO command when re-booting a program within itself. It's more of a lazy shortcut and reflects my work ethic in some projects and I find it rather burdensome even when these rare lapses-of-ethic occur. The GOTO command should really be used as little as the legendary(sarcasm activated) "SCANLINE" function within Blitz and should be regarded with it's almost negligible usage though, don't get me wrong there's always a time and a place to use a GOTO just make that in the past please (where is belongs), let the more specific and concise bits of code (FUNCTIONS) handle the burden, they're much faster.


Wings(Posted 2013) [#26]
Hi i write an example of how to use goto.
i have run the below code for 3 min and no stackoverflow accured ?
why ? shold i use gosub ?

;## Here is an example of how a idiot variable causes goto stak overflow.

.not_again

idiot=True

For i=1 To 100
If idiot=True Then Goto not_again
Next


Yasha(Posted 2013) [#27]
why ? shold i use gosub ?


Gosub uses the stack to store an address to return to. Since Goto doesn't return, it never uses the stack for anything, and therefore can't cause an overflow. (Try it, replace Goto with Gosub and the code will overflow almost immediately.)

As above, there is no reason to ever write new code that uses Gosub, because it's just a rubbish version of functions.

If you don't fully understand yet about stack frames and how call/return work and so on, it's a valuable subject to read up on.


Wings(Posted 2013) [#28]
thats good news.. some but warning for program locks with goto.