Why is Global considered bad?

Blitz3D Forums/Blitz3D Beginners Area/Why is Global considered bad?

Rob the Great(Posted 2011) [#1]
Hey all,

I've seen multiple posts where experienced programmers have mentioned that using Global variables isn't good practice and should be avoided if possible.

Is there a specific reason why? I don't overkill the Global variables in my programming, but I certainly use a lot of them only because it seems that there's no other way to share those variables throughout the whole program, including more than one function and whatnot (e.g. a Global variable for the amount of life remaining which is accessed many times throughout the game.)

As a self-taught programmer, I've always done things my own way, but if I ever considered working with a team, I would want them to be able to read and understand my code easily.


Yasha(Posted 2011) [#2]
I've seen multiple posts where experienced programmers have mentioned that using Global variables isn't good practice and should be avoided if possible.


You sure it's not just me doing that?

The reason is to do with side effects and referential transparency. In other words, what effects can a function have on the program as a whole that aren't completely described by the parameters and return value? Any such effects can't immediately be determined at the call site, and therefore it makes it harder to reason about that function's behaviour. The other thing is that functions exist to encapsulate operations; if the variables being used are shared, the operation isn't properly separated from the rest of the code logic. Not so bad when the procedure is doing something with a world-state thing like a global list or the main camera, but when minor temp variables are shared between procedures, there's no clear division of logic. It may make it impossible to express some structures altogether (e.g. recursion doesn't work if each invocation of the function can mutate the same variables).

It's basically the same as the argument against Goto. However, it's a lot less important, and a bit harder to follow. There are certainly a lot of good reasons to use Global variables in Blitz3D, not least because it doesn't support most of the alternatives (you need OOP or FP for that).

This is also definitely a minority opinion in Blitz, because of the language's focus. In games, there are plenty of places where it may be sensible for things to be global, such as the main camera, the "world" (however you define that), the sun, etc.


Rob the Great(Posted 2011) [#3]
lol, yeah, I did see your name a lot with those comments.

I see what you mean, though. I honestly hate going back to my Global list and making a new variable, so whenever I can, I use Local variables in the function, things that just get thrown away after the function ends.


_PJ_(Posted 2011) [#4]
I would agree, that in a general overall "industrial-standard-world" globals can be a "bad thing", but in B3D, there's at least 3 main reasons why they're necessary (actually thwese are very closely related):

1) Blitz doesn't support Static vars, and Constants must be declared as they are defined, and with other constants or literals, no constant function returns are supported either.

2) Globals save time and memory from repeating functions to retrieve certain values, such as millisecs() or longer, more intensive functions such as dealing with registry values

3) When dealing with entities, such as cameras or terrains or maybe images such as a backdropor HUD overlay, that are consistent and must be accessed by various parts of the program separately, it can be overkill I think, to send the handles of these if there's only ever one instance of them in your game for example.


jfk EO-11110(Posted 2011) [#5]
There are situations to use Globals and others for Locals, it's only a matter of a rational decision by the coder.

As long as a variable is used only by one function, it should be local. Nothing's wrong with Globals IMHO, if you use Globals only when it makes sense for some reason. There are a few things you should do when using globals:

-Use good, descripting names. In a Coproduction with other people you may use a personal prefix for your globals, constants and function names, eg.: RtG_ms%
-Always declare locals explicitely as locals (if you create a local only by using it, it may interefere with a global of the same name): Local i%=0

functions exist to encapsulate operations

I agree to some degree :) but I see the main (traditional) reason in: if your program is doing a certain thing more than once (if it's socalled redundant) during your main loop then you can save ram and work if you call a function instead.

In Speed-Optimized Assembler Code the programmers sometimes "unencapsulate" functions and put them back into the main loop code, even if it was called many times, only to prevent the overhead of jumping, stack handling etc. caused by the function call, eg given:

c=a
if a>b then c=b

is the same as
c=min(a,b)

but for such a simple action a function call may be considered an overhead, especially in speed critical sections.

My Conclusion: Globals are ok, but sometimes less is more.

Last edited 2011


Yasha(Posted 2011) [#6]
I agree to some degree :) but I see the main (traditional) reason in: if your program is doing a certain thing more than once (if it's socalled redundant) during your main loop then you can save ram and work if you call a function instead


That's not the only reason, bear in mind.

The name function comes from the mathematical term. In that sense, a function is an expression that takes some values and does something with them to produce a result value (this is why some languages distinguish between "functions" and "subroutines"). If you're writing a program in this sort of style (related to Functional programming), global variables can throw things off because the result value is no longer completely determined by the input values. Note that this doesn't apply to global constants, because if they can't be mutated by whatever happens within the function body then they also can't change the result from one call to another.

As a fan of functional programming, I tend to take this point of view. Blitz3D is most definitely not a functional programming language, and it's also more likely that your functions will actually be subroutines, that do something "to" something in order to change the state of the world - this is the most natural way to understand a game's main loop, after all, and games are what B3D is normally for. So Blitz3D has a lot of good reasons for using globals, yes (in fact, don't tell anyone I said this, but there are a few places where even Goto has its uses in B3D, due to the lack of certain other features like "continue").

I tend to adopt an extreme position simply to try to act as a counterbalance against the kind of sloppy code in the official examples, and that beginners often produce, where everything is global for no reason, the use of functions isn't fully understood, nobody declares variables etc. My hope is to influence people slightly into adopting a happy medium position where they write code that is still appropriate for B3D, but also cleanly structured and easy to read.

Last edited 2011


_PJ_(Posted 2011) [#7]
I suppoose, really, that understanding the deeper implications and effects of what you're doing when it comes to declaring globals etc. is the impoortant aspect.
What separates the "beginner" in Yasha's comment, is not so much a tendency for bad programming, but more the lack of understanding of the reasoning when and where to apply certain implementation according to the situation.

At the end of the day, I suppose, that so long as you (and presumably whoever else may be looking at or working with your code) understand fully what effect doing things in a certain way has, and that it's generally agreed to be the "best" (most memory efficient, fastest, easiest to follow - whatever's important) way to do so, and that's clearly your intent, then it should be fine.

Naturally, if you're working within a ffrramework where there are protocols and requirements, these can have a profound effect ion the decision.

Yeah, this could really be applied to any situation or environment, which each have their own limitations and requirements, so is quite generalised.


Rob the Great(Posted 2011) [#8]
@jfk
Wait, you can have a Global and a Local variable with the same name? I never knew that, but why on earth would anyone want to confuse the two? That sounds like a programming nightmare. And I'm a little confused as to how Blitz would know the difference. The only way I could see that working is if, say, Global x = 1 is declared early in the program, but later in a function, Local x = 2 is declared and Blitz disregards the Global x for the whole function. Is that accurate? Sounds like Namespace and Strict commands, which I've never liked. Still, I can't see a purpose for that, as I would much rather name the Local variable something different than the Global, like tempx or something.

I feel a lot better about my Global variables now. Mostly, I was just concerned that using Globals would cause problems with my game or something, but from what I've gathered from this topic, I seem to be on the right track with how/when to use Globals.


Yasha(Posted 2011) [#9]
Local x = 2 is declared and Blitz disregards the Global x for the whole function.


Yep, that's what happens. Note that it only applies within defined functions; you can't have a local shadow a global in the outermost scope, because globals are declared there too and it would count as a duplicate declaration. The issue jfk mentions about interfering names is unlikely but possible, especially in large projects with code written by many people. (One more reason why I recommend IDEal to all: it highlights declared locals, globals, functions, types, and undeclared names all in different colours, so you can quickly see what names do.)

Just for fun, look what Blitz3D's namespace rules let you do if you want to really upset other people:
Type Rocket
	Field x, y, z
	Field rocket.Rocket
End Type

Global Rocket.Rocket = New Rocket

Function Rocket.Rocket(Rocket.Rocket)
	If Rocket\rocket<>Null
		Return Rocket\rocket
	Else
		Return Rocket
	EndIf
End Function

Rocket = Rocket(Rocket)


Functions, types, globals, locals and fields all inhabit separate namespaces (actually there are good reasons why one might want to take advantage of this... but that's advanced stuff).

Last edited 2011


Rob the Great(Posted 2011) [#10]
Type Rocket
	Field x, y, z
	Field rocket.Rocket
End Type

Global Rocket.Rocket = New Rocket

Function Rocket.Rocket(Rocket.Rocket)
	If Rocket\rocket<>Null
		Return Rocket\rocket
	Else
		Return Rocket
	EndIf
End Function

Rocket = Rocket(Rocket)

Hehe, I have to admit, that's awesome to look at. Confusing, but fun to try and decipher all the same.


jfk EO-11110(Posted 2011) [#11]
The issue jfk mentions about interfering names is unlikely but possible

It usually happens when you are introducing a new local variable in a function, but you're too lazy to declare it (when you just start using it inside a function, it will be local), then later on you introduce a global of the same name - the one that was local before will become global now, altering the state of your global when the function is executed, probably causing problems.


Zethrax(Posted 2011) [#12]
Personally I just add a 'G_' header to all my global variable identifiers. No problem with global-local conflicts then, and I can see right away that I'm dealing with a global and not a local with the same name.


Yasha(Posted 2011) [#13]
Personally I just add a...


It's most likely to be a problem when more than one person's code is in the project, usually because you're including someone's premade library.

Anyway, if the functionality wasn't there to let locals shadow globals, it would be pretty hard to include someone else's code without a huge risk of either it not working as expected, due to invisibile interference, or errors with declaration (sure you can still inadvertently name a top-level function or global thingy the same, but that's going to be a lot easier to correct than either going through every single temp var, or them putting a namespace tag on every single temp var).