4 ms·
Global mutable variables are generally a bad idea because they have nonlocal side effects which are difficult to mitigate, like aliased mutable pointers but wor
by nyanpasu64 5y ago
Global mutable variables are generally a bad idea because they have nonlocal side effects which are difficult to mitigate, like aliased mutable pointers but worse. Extra non-aliasing arguments and multiple return values are free of these issues, and a better tradeoff in almost all cases (unless you're sure you'll never run 2 instances of a system in the same address space, and you have specialized constraints possibly including embedded/safety-critical development and emulators).
When experienced programmers utilize global variables whenever it makes sense, it tends to bite future generations. Windows's GetLastError is a mess (some functions set it on error but don't clear it on success), I'm not sure about POSIX's errno, and when threads were introduced, these variables had to be changed to thread-local state.
- kayodelycaon 5y ago> When experienced programmers utilize global variables whenever it makes sense, it tends to bite future generations. Usually. There is one pattern I keep using in Rails to inject the current_user for the request into the model layer. At the beginning of a request, Thread.current[:current_user] gets set to the current_user. At the end of the request, it's cleared. For those unfamiliar, each Thread has its own Hash object and [] and []= instance methods to access it. It's a way to create global variables scoped to the current thread. To hide this implementation detail, I wrap this functionality in ApplicationRecord.current_user and ApplicationRecord.current_user= methods so no one accidentally uses :curent_user. The most common use case for this is to automatically set created_by and updated_by on a model, the same way created_at and updated_at are set. Cron jobs can do something like ApplicationRecord.current_user = User.cron_user if needed. Ideally, I would set this in a CronTask parent class so current_user is always available in the model layer. I always document these methods and explain exactly what is happening and why. Abstracting away from the implementation detail mitigates most of the problems with a global variable. Obviously, the Thread.current method does not work at all if you're using an event or actor driven architecture. Rails does a thread (or process) per request, so this works extremely well.
- lucumo 5y ago> It's a way to create global variables scoped to the current thread. That's not a global variable; that's a thread-scoped variable. (It's also not (really) mutable. It gets initialized at the start of the scope and gets removed at the end of scope.)
- ww520 5y agoFor service directory lookup, global variable is fine. For global scope queue, global variable is fine. For global scope logging service, global variable is fine.
- sagichmal 5y agoEach of these things prevents isolated testing of dependent components. For that reason alone they are nonstarters.
- ori_b 5y agoglobalvar = new NopImpl() works fine for testing. If you squint, you can even think of dependency injection frameworks like Dagger as a lot of boilerplate around a global database of instantiations.
- deleted 5y ago[deleted]
- dcow 5y agoUntil you try to run two tests at once, one which requires NopImpl and one which requires the real impl.
- sagichmal 5y agoAs dcow pointed out, it really doesn't work fine for testing, as it couples what should be isolated tests together, and prevents them from being executed concurrently.
- User23 5y agoThis is one of the reasons GNU Emacs will probably never be symmetrically multithreaded.
- xorcist 5y agoThat big backing relational database of yours contains tables of "global mutable variables". The non local side effects are part of the point.