Live data from Hacker News

Global variables are not the problem

codestyleandtaste.com

121–130 of 165 posts

Re: Global variables are not the problem

#121
post #99

From the article> "Static Function Variable: In C inside a function, you can declare a static variable. You could consider this as a local variable but I don't since it's on the heap, and can be returned and modified outside of the functions. I absolutely avoid this except as a counter whose address I never return." These variables are not on the heap. They are statically allocated. Their visibility is the only thing…

> These variables are not on the heap. They are statically allocated. Their visibility is the only thing that differentiates them from global variables and static variables defined outside of functions. In C++, there is a difference: function-static variables get initialized when control first passes into the function. That difference matters in cases where the initial value (partly) comes from a function call or fro…

> If they were to design that today, I think they would require the initialization expression to be constexpr.

Why would they?

This (non-constexpr) semantic would be useful as lazy initialization. .. and C++ love tiny little features like these.

Re: Global variables are not the problem

#122
post #90

Earlier quoted context omitted.

I got into this argument with my former coworkers. Huge legacy codebase. Important information (such as the current tenant of our multi-tenant app) was hidden away in thread-local vars. This made code really hard to understand for newcomers because you just had to know that you'd have to set certain variables before calling certain other functions. Writing tests was also much more difficult and verbose. None of these…

You know what helps manage all this complexity and keep the state internally and externally consistent? Encapsulation. Provide methods for state manipulation that keep the application state in a known good configuration. App level, module level or thread level. Use your test harness to control this state. If you take a step back I think you’ll realize it’s six of one, half dozen of the other. Except this way doesn’t…

These methods existed. The problem was that when you added some code somewhere deep down in layers of layers of business code, you never knew whether the code you'd call would need to access that information or whether it had already previously been set.

Hiding state like that is IMHO just a recipe for disaster. Sure, if you just use global state for metrics or something, it may not be a big deal, but to use it for important business-critical code... no, please pass it around, so I can see at a glance (and with help from my compiler) which parts of the code need what kind of information.

Re: Global variables are not the problem

#123
post #89

Earlier quoted context omitted.

And do you always know beyond any reasonable doubt that your code will be single-threaded for all time? Because the moment this changes, you're in for a world of pain.

If JavaScript ever gets real shared memory multithreading, it will be opt in so that the entire web doesn’t break. So, yes.

If the discussion here is meant to be solely about JavaScript, then I'll happily consider all my comments in this thread to be obsolete, since I don't have particularly strong opinions about that language since I don't use it a lot.

I was under the impression that many people here were discussing the usage of global variables more generally, though.

Re: Global variables are not the problem

#124

Earlier quoted context omitted.

You know what helps manage all this complexity and keep the state internally and externally consistent? Encapsulation. Provide methods for state manipulation that keep the application state in a known good configuration. App level, module level or thread level. Use your test harness to control this state. If you take a step back I think you’ll realize it’s six of one, half dozen of the other. Except this way doesn’t…

These methods existed. The problem was that when you added some code somewhere deep down in layers of layers of business code, you never knew whether the code you'd call would need to access that information or whether it had already previously been set. Hiding state like that is IMHO just a recipe for disaster. Sure, if you just use global state for metrics or something, it may not be a big deal, but to use it for i…

I’m having a difficult time understanding the practical difference between watching an internal state object vs an external one. Surely if you can observe one you can just as easily observe the other, no?

Surely if you can mutate a state object and pass it, its state can get mutated equally deep within the codebase no different than a global, no?

What am I missing here? To me this just sounds like a discipline issue rather than a semantic one.

Re: Global variables are not the problem

#125
Global variables are a programming construct, which like other programming constructs is neither bad nor good. Except, due to the takeover of workplaces by the best practices cult, instead of reasoning about tradeoffs on a case by case basis (which is the core of coding and software engineering), we ascribe a sort of morality to programming concepts.

For example, global variables have drawbacks, but so does re-writing every function in a call-stack (that perhaps you don't control and get callbacks from).

Or if you are building a prototype, the magic of being able to access "anything from anywhere" (either via globals or context, which is effectively a global that's scoped to a callstack), increases your speed by 10x (since you don't have to change all the code to keep passing its own abstractions to itself as arguments!)

Functions with long signatures are tedious to call, create poor horizontal (which then spills over to vertical) code density. This impacts your ability to look at code and follow the logic at a glance, and perhaps spot major bugs in review. There's also fewer stuff for say copilot to fill in incorrectly, increasing your ability to use AI.

At the end, every program has global state, and use of almost every programming construct from function calls (which may stack overflow) or the modulus operator (which can cause division by zero), or sharing memory between threads (which can cause data races) requires respecting some invariants. Instead, programmers will go to lengths to avoid globals (like singletons or other made up abstractions -- all while claiming the abstractions originate in the problem domain) to represent global state, because someone on the internet said it's bad.

Re: Global variables are not the problem

#126

Earlier quoted context omitted.

static variable addresses are an extremely important tool in static analysis, for proving certain program invariants hold. In C, a const static often will be able to tell you more about the structure of your code at runtime than a #define macro ever could! though unless you're programming extremely defensively (eg trying to thwart a nation state), I see no reason why you would use them at runtime

Could you expand on this? I don't understand the point you're making, or how it's useful.

Perhaps a distinction between static analysis (at compile) and dynamic analysis (at run time) is useful ?

Re: Global variables are not the problem

#127

Earlier quoted context omitted.

static variable addresses are an extremely important tool in static analysis, for proving certain program invariants hold. In C, a const static often will be able to tell you more about the structure of your code at runtime than a #define macro ever could! though unless you're programming extremely defensively (eg trying to thwart a nation state), I see no reason why you would use them at runtime

Could you expand on this? I don't understand the point you're making, or how it's useful.

static consts in C carry their identity through their (fixed, unchanging) pointer address. Lets say you have a business rules engine, that's only meant to ingest threshold values from a certain module. You want to know if the 3.0 you're using is coming from the correct place in the code, or if there's a programming error. With a define, there's not enough additional information to be able to. With a static const, you can just have a const static array of pointers to the valid threshold constants, and use that in your tests for the rules engine.

I work in a highly regulated field, so often this level of paranoia and proof helps us make our case that our product works exactly the way we say it works.

Re: Global variables are not the problem

#128
post #83

I find the concept of a context structure passed as the first parameter to all your functions with all your "globals" to be very compelling for this sort of stuff.

That's only needed if you use multiple threads. In a single thread global vars are just fine, and much simplier than passing around ctx

Even in a single-threaded environment, passing a struct around containing values allows you to implement dynamic scoping, which in this case means, you can easily call some functions with some overridden values in the global struct, and that code can also pass around modified versions of the struct, and so on, and it is all cleanly handled and scoped properly. This has many and sundry uses. If you just have a plain global variable this is much more difficult.

Although Perl 5 has a nice feature where all global variables can automatically be treated this way by using a "local" keyword. It makes the global variables almost useful, and in my experience, makes people accidentally splattering globals around do a lot less damage than they otherwise would because they don't have to make explicit provision for scoped values. I don't miss many features from Perl but this is on the short list.

Re: Global variables are not the problem

#129

I think this article broadens the definition of global variable and then says "Look, the things I added to the definition aren't bad, so global variables aren't always bad." If you just look at what people normally mean by global variable, then I don't think the article changes minds on that.

To make it worse: "Look, the things I added to the definition aren't bad in this specific language and use-case, so global variables are not the problem".

To me, the author either has a very narrow field of focus and honestly forgets about all the other use-cases, practicalities and perspectives, or they choose to ignore it just to fire up a debate.

In any case, these constructs are only true for JavaScript (in node.js whose setup avoids threads common issues), and fall flat in a multithreaded setup in about every other languages.

If I were to port this to Rust, first, the borrow checker would catch the described bugs and not allow me to write them in the first place. But secondly, if I really insist on something global that I need to mutate or share between threads, I can do so, but would be explicitly required to choose a type (Mutex, RwLock, with Arc or something) so that a) I have thought about the problem and b) chose something that I know to work for my case.

Re: Global variables are not the problem

#130

Earlier quoted context omitted.

These methods existed. The problem was that when you added some code somewhere deep down in layers of layers of business code, you never knew whether the code you'd call would need to access that information or whether it had already previously been set. Hiding state like that is IMHO just a recipe for disaster. Sure, if you just use global state for metrics or something, it may not be a big deal, but to use it for i…

I’m having a difficult time understanding the practical difference between watching an internal state object vs an external one. Surely if you can observe one you can just as easily observe the other, no? Surely if you can mutate a state object and pass it, its state can get mutated equally deep within the codebase no different than a global, no? What am I missing here? To me this just sounds like a discipline issue…

> To me this just sounds like a discipline issue rather than a semantic one.

Using an explicit parameter obviates the need for discipline since you can mechanically trace where the value was set. In contrast, global values can lead to action at a distance via implicit value changes.

For example, if you have two separate functions in the same thread, one can implicitly change a value used by the other if it's thread-local, but you can't do that if the value is passed via a parameter.

Post reply on HN