January 25, 2022

On Tuesday, 25 January 2022 at 09:53:25 UTC, rempas wrote:

>

So can someone make examples about how global variables can mess me up.

Global State: a Tale of Two Bad C APIs

January 25, 2022

On Tuesday, 25 January 2022 at 11:43:56 UTC, rempas wrote:

>

On Tuesday, 25 January 2022 at 10:22:59 UTC, max haughton wrote:

>

Passing state down a huge chain is also a code-smell. Writing code that uses a "global" variable is only slightly better than using a global variable.

What is the rough outline of these functions?

What "rough outline" of a function means? Sorry if that sounds but I need to learn.

What I mean by outline (where global is the thing you don't like passing around) :

```d
int foo(ref int global)
{
    bar(global);
}
int bar(ref int global);
```
the "outline" (a reduced call graph if you will) would be something like

```
foo(ref int global)
  bar(global);
```
if the code is small enough just post the code, I want to illuminate that if you have a bunch of parameters everywhere you might want to make it a member function instead.
January 25, 2022

On Tuesday, 25 January 2022 at 09:53:25 UTC, rempas wrote:

>

It is known that people dislike global variables and the reason is that they make the code harder to debug. In my experience tho, it is the exact opposite. When I have a variable that I must pass down to 5-6 functions, I find it much easier to make it global rather than having it been passed in all the functions that need it. [snip]

I just made use of globals in a non-D project at work. Basically, I was tasked with evaluating a few different options for how to do something. There was already an existing code base. For me to loop through the options at a high level would require passing these variables through to multiple functions and making more modifications to the code base than I had wanted to. Instead, I created some globals covering the different options so that I could get everything working in the functions that I actually needed to change and easily test what the results were with different options. When the final option was decided, I removed the globals and all code associated with the rejected options.

In D, I could have used a __gshared enum* and then used static if on it to switch the code paths, which would be nicer than the language I was working in.

In retrospect, an alternative could have been to create separate branches and then just delete the ones that didn't work out. The downside to that is that there was code that was used between the different options and as I was trying things out I may have changed code that all three would have used, so then I would have had to update multiple branches with the changes. I'm sure there is a way that git could handle that for me, but it might have been a little more work. For a bigger change than I was making, it might have been worth it to rely on git.

  • For instance:
import std.stdio: writeln;
enum X {A, B, C}
__gshared enum X x = X.B;

void foo() {
    static if (x == X.B)
        writeln(x);
}

void main() {
    foo();
}
    ```
January 25, 2022

On Tuesday, 25 January 2022 at 14:20:28 UTC, jmh530 wrote:

>

[snip]

  • For instance:

The star should be associated with this...

January 25, 2022
On Tue, Jan 25, 2022 at 09:53:25AM +0000, rempas via Digitalmars-d wrote:
> It is known that people dislike global variables and the reason is that they make the code harder to debug.

The problem with global variables is that firstly, it modifies the behaviour of a function apart from its parameters. This in itself may not seem like a big deal, but a global variable also means it can be modified by anyone and everyone at any time.  So you may have a call to function F from module A, but you can't tell what the result will be, because it depends on whether module B modified the global variable before you called F.  Changing the order you call a pair of functions may drastically change their behaviour in unexpected ways if they both modify the same global variables.

In a small project this is not a big deal, but in a large project it makes it almost impossible to debug, because how F will behave changes depending on the entire code path leading up to the call to F (whether anybody along that path changed the global variable and what they changed it to).  When you're faced with a bug report, you generally don't have enough information to tell exactly which path the code took before it got to F, which also means you can't tell how F will behave. When this happens not just for one function F, but for every other function in the code where the bug is, it makes debugging an extremely unpleasant experience.

Another problem with global variables is that there can only be *one* instance of that global. Suppose you have a database-driven program where basically every function needs to access the database connection. Well, why not make it a global variable? Easy peasy.  Well, then one day, you decide that you need to add a second database connection (maybe for syncing, backup, or merging, whatever). Now what? ALL of your functions assume that only a single connection exists. To add a second connection you now have to rewrite every single function in your program (and/or hack it by swapping the single global variable between two connections -- very error prone and almost inevitably leads to tons of bugs and impossible situations).  Had you written your functions to take the database connection as a parameter, inconvenient as it may be, it would have been a trivial code change: just pass the 2nd connection and you're done.


> In my experience tho, it is the exact opposite. When I have a variable that I must pass down to 5-6 functions, I find it much easier to make it global rather than having it been passed in all the functions that need it.  This practice also makes my function signatures looking much cleaner.  Yeah, one variable will not make a difference but in my project, I have about 2-3 variables that need to be passed down to a lot of functions so I only think that it makes sense to use them as globals.

Whenever you have multiple shared parameters between a bunch of functions, that's a sign that they probably should be member functions of a common struct or class.  The shared parameters should be in the struct, then you don't have to explicitly pass them around, you just add another function to the struct and they gets passed implicitly for you.


> Another problem is the case that I'll introduce a new variable that needs to also be passed in most of my functions. What happens then? Let's say I will have 20 functions at the time. I have to change both the function signature and all the other places in code that call this function. The latter can be easily done with a quick "search and replace" in my text editor but still, it's a tedious thing to do.

Either group your parameters in a single struct, or make your functions members of the struct. Then all you have to do is add another field to your struct and you're done. No tedium.

Using a struct/class also takes care of the instancing problem: if one day you suddenly need a different set of parameters, you just create a different instance of your struct and everything Just Works(tm). If they were global variables, then there can only be one instance of every variable, and you're stuck up the creek without a paddle.


T

-- 
Don't drink and derive. Alcohol and algebra don't mix.
January 25, 2022

On 1/25/22 4:53 AM, rempas wrote:

>

It is known that people dislike global variables and the reason is that they make the code harder to debug.

That's not the reason. Global variables are hard to control and review. That is, if a variable is global, how do you know it's not misused? The point of not using global variables is to reduce the surface area of the program that needs review to ensure it's correctly used.

In some cases, they make sense, and in those cases, I'd recommend at least guarding the direct access to the variable through properties so you can control how it's used.

>

In my experience tho, it is the exact opposite. When I have a variable that I must pass down to 5-6 functions, I find it much easier to make it global rather than having it been passed in all the functions that need it.

In D, I would do this one of 2 ways:

  1. Declare the variables in a function, then run your algorithms inside inner functions. They all now have access to the variable. I'm surprised at how many complex problems and APIs become super-straightforward when you start using local functions (even templated ones).

  2. Enclose the data and methods in a struct, even if that struct is a struct inside a function. This is a necessity if you have mutually recursive functions, since a local function cannot call another local function that hasn't been defined yet.

-Steve

January 25, 2022

On Tuesday, 25 January 2022 at 09:53:25 UTC, rempas wrote:

>

It is known that people dislike global variables and the reason is that they make the code harder to debug.

I object to your question. There's not necessarily anything wrong with global variables, for loops, goto statements, and whatever else some claim you shouldn't use. Maybe you're thinking of Haskell.

I don't think it's common that you need global variables in D. If you want to share a variable among several functions, use a struct. But if a global is the best solution, use the global.

If you've ever had the joy of working with FORTRAN 77 or earlier versions of FORTRAN (back when it was in all caps) you understand why working with globals can be a traumatic experience. For instance, multiple functions in multiple files will sometimes change the same global variable.

You have global variable x. foo calls bar which calls baz which changes x. Then 75 lines later foo calls goo, which also changes x. Even if there's no bug in your code, this style of programming will burn threw all your brain energy in a hurry. You have to think about the entire program in order to reason about one part. It was often easier to rewrite the whole thing from scratch than to make a meaningful change involving global variables.

January 25, 2022
On 1/25/22 01:53, rempas wrote:

> people dislike global variables

The situation is much better in D because I suspect what you call global is D's module-scope and thread-local.

(Note: I don't want to argue whether thread-local by default was a good decision or not but I certainly take full advantage of the ease of thread-local variables in D.)

So, module-scope variables are just fine: std.parallelism uses a default ThreadPool object, std.stdio functions use stdout, in this case a truly global object, etc.

> When I have a variable that I must pass down to 5-6
> functions, I find it much easier to make it global rather than having it
> been passed in all the functions that need it.

I've used that method once, which I mentioned at this point during a presentation:

  https://youtu.be/dRORNQIB2wA?t=2946

Both you and I realize that a module is an object and in our case there is a single object of it. That works.

When you need more than one object (more than one context), then you move all those variables to a user-defined type and they become proper member variables.

> global variables can mess me up

Otherwise, as everybody else told, global variables can be very dangerous:


https://www.safetyresearch.net/toyota-unintended-acceleration-and-the-big-bowl-of-spaghetti-code/

That report mentions "thousands of global variables", all of which I bet started as "what can go wrong?"

Ali

January 25, 2022
On Tuesday, 25 January 2022 at 15:47:12 UTC, Ali Çehreli wrote:
> ... https://www.safetyresearch.net/toyota-unintended-acceleration-and-the-big-bowl-of-spaghetti-code/...


This is a good read, other than this paragraph:

"Other egregious deviations from standard practice were the number of global variables in the system. (A variable is a location in memory that has a number in it. A global variable is any piece of software anywhere in the system can get to that number and read it or write it.) The academic standard is zero. Toyota had more than 10,000 global variables."

I couldn't help feeling like the article was describing a particular .NET project my job has me assigned to.
January 25, 2022
On Tue, Jan 25, 2022 at 07:47:12AM -0800, Ali Çehreli via Digitalmars-d wrote: [...]
> Otherwise, as everybody else told, global variables can be very dangerous:
> 
> https://www.safetyresearch.net/toyota-unintended-acceleration-and-the-big-bowl-of-spaghetti-code/
> 
> That report mentions "thousands of global variables", all of which I bet started as "what can go wrong?"
[...]

Further down the article it states "Toyota had more than 10,000 global variables."  That's... wow.  They must have inherited that code from the 80's or somewhere thereabouts, when this was the accepted way of coding. By today's standards, I should be surprised there aren't *more* accidents than there were(!).

And also, the other points in the article about failsafes and single points of failure, all jive with what Walter has been telling us about airplane design. :-D


T

-- 
If you think you are too small to make a difference, try sleeping in a closed room with a mosquito. -- Jan van Steenbergen