Earlier quoted context omitted.
I could not initially reply to you. Your comment rubbed me the wrong way, because I had no intention of trying to degrade anyone, and frankly, I was offended. But I thought better of my hasty and emotional response. I would rather take a deep breath, re-focus, re-engage, and be educated in a thoughtful dialog than get into a mud slinging contest. I am always willing to be enlightened.
A tip, in your profile you can set a delay which is a number of minutes before your comments will become visible to other people. Mine is set to 2 right now. This gives you time to edit your comment (helpful for some longer ones) but also to write some garbage response and then think better and delete it before anyone's the wiser. It's also helpful to give you time to re-read a mostly good, but maybe not polite, resp…
Global variables are not the problem
111–120 of 165 posts
Re: Global variables are not the problem
#112I 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 exactly why I used this specific example. I seen many code bases that use clone to avoid mutation problems so I wrote this specifically to show it can become a problem too. I wrote a better article on globals. I plan on posting it next week
With that said, it seems obvious that if you want to globally count the calls, then that count shouldn’t live in an argument where you (the function) don’t control its lifetime or how global it actually is. Simple has no say over what object obj.counter points to, it could trivially be a value type passed into that particular call, so if you know you want a global count then of course storing it in the argument is the wrong choice.
Global has two conflated meanings: global lifetime (ie lifetime of the whole program) and global access (which the article states). Simple needs global lifetime but not global access.
You rarely ”need” global access, although for things like a logger it can be convenient. Often you do need global lifetime.
Re: Global variables are not the problem
#113Global variables (in languages where they otherwise make sense and don't have footguns at initialization and whatnot) have two main problems: 1. They work against local reasoning as you analyze the code 2. The semantic lifetime for a bundle of data is rarely actually the lifetime of the program The second of those is easy to guard against. Just give the bundle of data a name associated with its desired lifetime. If y…
I don't think #1 is necessarily true. Take a common case for a global variable, a metric your app is exposing via prometheus. You have some global variable representing its value. Libraries like to hide the global variable sometimes with cuteness like MetricsRegistey.get_metric("foo") but they're globally readable and writable state. And in your function you do a little metric.observe_event() to increment your counte…
On the other hand, I have seen quite a lot of parsing/compilers' code from the seventies and eighties and let me tell you: for some reason, having interfaces between lexer and parser, or lexer and buffered reader, or whatever else to be "call void NextToken(void), it updates global variables TokenKind, TokenNumber, TokenText to store the info about the freshly produced token" was immensely popular. This has gotten slightly less popular but even today e.g. Golang's scanner has method next() that updates scanner's "current token"-related fields and returns nothing. I don't know why: I've written several LL(1) recursive-descent parsers that explicitly pass the current token around the parsing functions and it works perfectly fine.
Re: Global variables are not the problem
#114Re: Global variables are not the problem
#115Earlier quoted context omitted.
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
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.
Much worse is protecting concurrent writes, eg to an object or hash table
Re: Global variables are not the problem
#116Earlier quoted context omitted.
I like the diagnosis. My JS is terrible, but it seems like once you make the counter a global variable it is just better to change it to have an atomic dedicated count function. So instead of incrementing the counter in simple, a globalCount() function gets called that isolates the state. Something like { let i = 0; var counter = function (){ console.log(++i); } } Then call counter() to count & log and document that…
How is a globally-scoped closure not a global variable?
Nearly everything in RAM is technically a global variable to someone who is keen enough. Programming depends on a sort-of honour system not to treat things like variables if it is hinted that they shouldn't and it doesn't make sense to. Otherwise we can all start making calls to scanmem & friends.
Re: Global variables are not the problem
#117Earlier quoted context omitted.
Hard disagree. If I have 500 functions, I don't want to extrapolate out the overhead of passing a state object around to all of them. That's a waste of effort, and frankly makes me think you want to code using an FP paradigm even in imperative languages. Module-level and thread-level "globals" are fine. You gain nothing (other than some smug ivory tower sense of superiority) by making your functions pure and passing…
Yes, you gain testability. If your global state contains something that runs in prod but should not run in a testing environment (e.g. a database connection), your global variable based code is now untestable. Dependency Injection is popular for a very good reason.
If you have something that should only run in testing, perhaps your test harness should set the global variable appropriately, no?
Re: Global variables are not the problem
#118I 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 just seems like globals with extra steps. Suddenly if your context structure has a weird value in it, you’ll have to check every function to see who messed it up.
Re: Global variables are not the problem
#119Earlier quoted context omitted.
Hard disagree. If I have 500 functions, I don't want to extrapolate out the overhead of passing a state object around to all of them. That's a waste of effort, and frankly makes me think you want to code using an FP paradigm even in imperative languages. Module-level and thread-level "globals" are fine. You gain nothing (other than some smug ivory tower sense of superiority) by making your functions pure and passing…
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…
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 require manually passing an object into every function in your codebase.
Re: Global variables are not the problem
#120Moving state into the global namespace and accessing it directly from that space makes it much more difficult to test, instrument and integrate.
Sure, if you're building disposable toy software, do whatever is easiest. But if you're building software for others to use, at least provide a context struct and pass that around when you can.
For those cases where this is challenging or impossible, please sequence your application or library initialization so that these globals are at least fungible/assignable at runtime.