17 Apr 2021
In my experience, a disproportionate amount of bugs in NetLogo models are caused by the mismanagement of global variables. Here are a few reasons why I think that is the case, in (roughly) decreasing order of importance.
Code is not just a way of getting the computer to do what you want it to do—it is a way of conveying meaning to the reader of your code. When you declare a global variable, the meaning you convey is: “Hey! This thing is important throughout the whole program. You should always be paying attention to it.”
Sometimes it’s justified , but very often, it is not. An unjustified global is not only failing to convey the right message: it’s actively misleading.
When I approach a model’s code for the first time, the first thing I do is look at the declarations on top: what breeds do we have, what agent variables do we have, and what global variables do we have? That gives me the “big picture” and tells me what I should be paying attention to when trying to understand what the code is doing. Each time you add one thing in there, you increase the size of the “big picture” and the difficulty of paying attention to it all. Globals carry a significant cognitive cost.
Maybe you don’t have to pay attention to everything but you don’t know that yet. You can only rely on what the author tells you.
Corollary to the previous point, procedures that don’t use globals have many advantages:
They are easier to understand. You can keep them isolated in your mind and concentrate on what’s happening inside the procedure. You don’t have to worry that a global variable doesn’t have the right value because it has been changed somewhere else.
They are more robust: you cannot break them by modifying a global variable.
They are more reusable: you can more easily use them in another model, or even at some other place in the same model, because they are not dependent on a global variable having a particular value at a particular time.
Globals make testing much more tedious. NetLogo models don’t have true unit tests, but as part of model verification, it is often useful to write short test procedures or just play around in the command center to make sure a procedure works as intended. If the procedure uses globals, you need to make sure it works with all possible combination of values for these globals too. They’re like “hidden parameters” for the procedure, and it’s very easy to forget about them.
Globals pollute the name space. Since pretty much all identifiers must be unique in NetLogo, once you have a global variable, you cannot reuse its name for anything else. It’s a minor but surprisingly frequent annoyance. It can also cause conflicts with
__includefiles, though these are not often used. This all ties up with the fact that NetLogo doesn’t offer any modularity above the procedure level.
There are situations in NetLogo where it is very hard to reason about what code runs in what order (e.g., when two “forever” buttons run at the same time). Reasoning about global variables in those situations is even more difficult.
There is a slippery slope at work here. Maybe at first you’re thinking: “It’s no big deal. I only have three globals and my model is so small that it’s easy to keep track of everything.” But models grow. (“What’s just one more global?”) Or they become the basis for multiple versions of the original model, where these globals are used in slightly different ways or sometimes not at all because it is easy to forget to remove an unused global. Minimizing the use of globals right from the start keeps us a step ahead in the fight against entropy.
If you google “global variables”, the first link that come up are the Wikipedia article (“They are usually considered bad practice precisely because of their non-locality”), CS101 lecture notes (“WARNING Global variables are not generally used. It is a contradiction of modularity to have variables be accessible in functions in which they are not used.”) and a page called Global Variables Are Bad. There also exists Wulf and Shaw (1973) Global variable considered harmful.
Still, sometimes, they are the right tool for the job. I would simply like NetLogo modelers to think of a global as something you “spend” rather than just declare for free. If, after careful consideration, you think that a particular situation warrants the complexity and the cognitive cost of using a global then, by all means, treat yourself to one!