January 25, 2022
On Tue, Jan 25, 2022 at 04:19:31PM +0000, Kyle via Digitalmars-d wrote:
> 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.

Don't worry, it happens more frequently than most people would like to admit in "enterprise" software.  After having dealt with "enterprise" code for several decades, I do not have many good things to say about it. I often like to allude to one particular project I had the misfortune of being assigned to, where on one occasion I had to go through 6 layers of abstraction just to make a single function call -- one layer of which involved fwrite() of the arguments into a *temporary file* (yes, in /tmp/) and fread()'ing it from the other end of an RPC call.  (Happily, the component that that particular piece of code was in has since been trashed and rewritten from ground up -- without those egregious useless layers of abstraction.)

And unfortunately, "deviations from standard practice" are IMO not an accurate description of the industry; IME spaghetti code with tons of global variables and other code smells *are* the de facto standard practice in "enterprise" code, esp. those of significant size and worked on by large teams of programmers.


T

-- 
Don't modify spaghetti code unless you can eat the consequences.
January 25, 2022
On Tuesday, 25 January 2022 at 15:47:12 UTC, Ali Çehreli wrote:
> 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/
>
Toyota, lol. I also had once to interact with software coming from them. It was surpzising to say it politely. It was a order system for suppliers of parts. The orders were tickets the suppliers had to scrape from a website. The very uncommon feature of the site was that it checked credentials (username/password) for the connection but didn't check if the client managed to read the ticket or not. Any read attempt of the tickets url would delete it on the server. The problem was that there was no "developer" server, only the real production server and I had to write the client software without losing orders as these were real orders of parts the supplier. It was impossible to negotiate with T. that there system was stupid. I managed to only lose 4 orders which amounted to several thousands of euros.

January 25, 2022
On Tue, Jan 25, 2022 at 05:10:40PM +0000, Patrick Schluter via Digitalmars-d wrote:
> On Tuesday, 25 January 2022 at 15:47:12 UTC, Ali Çehreli wrote:
[...]
> Toyota, lol. I also had once to interact with software coming from them. It was surpzising to say it politely. It was a order system for suppliers of parts. The orders were tickets the suppliers had to scrape from a website.  The very uncommon feature of the site was that it checked credentials (username/password) for the connection but didn't check if the client managed to read the ticket or not. Any read attempt of the tickets url would delete it on the server.

And this, ladies and gentlemen, is a prime example of why the range API should not conflate .front with .popFront. ;-)


> The problem was that there was no "developer" server, only the real production server and I had to write the client software without losing orders as these were real orders of parts the supplier. It was impossible to negotiate with T. that there system was stupid. I managed to only lose 4 orders which amounted to several thousands of euros.

Ah yes, the good ole read-once-only system. It's starting to sound more and more like a legacy system inherited from the 70's or 80's where such weird practices were commonplace. Probably a 90's website bolted on top of a 70's legacy system, inextricably linked to the single production server so it cannot be cloned into a staging server but only works with the real one.  Wouldn't be surprised if the developers actually *worked* on the real server and don't even have a testing server to test their changes on.


T

-- 
Why have vacation when you can work?? -- EC
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.

Assuming you mean mutable global state. Immutable global data is okay IMO.

Because purity is convenient. When I call a function, let's say field.initializeUnit(12, 15) it's so much nicer and tidier if I can rest assured it won't affect anything else than field, and if I know it acts the exact same way every time, when the arguments are similar.

Now consider if field was a global variable. Okay, initializeUnit(12, 15) is shorter than field.initializeUnit(12, 15). But I have to set the field I want to manipulate in advance, theField = /*whatever*/. And when reading code that acts like this, I do not know what affects what without peeking what each of the called functions do. Did the initializeUnit use theField or thatOtherField? With the pure version I can instantly see it's the field variable I'm using. The non-pure version is much uglier and harder to read after all, despite having a shorter argument list.

If the argument lists get lengthy, it is sometimes worth to collect the argument sets into structs or classes that have most of the needed data in one place. You might also consider the with statement. But globals just plain blow.

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. I know that probably everyone here has more personal experience than me so I really want to learn why global variables are considered so harmful.

// ---

module test;
import std;

int x = 0;

void main()
{
    writeln(foo);
}

int foo()
{
    int x = 10;

    if(x) // ooh. you sure this was your intention??
        return x;
    else
        return .x;
}

// ---
January 26, 2022

On Tuesday, 25 January 2022 at 12:07:31 UTC, Ola Fosheim Grøstad wrote:

>

Because the program is larger, there are more locations that could mutate the global state. ("reason" ≈ "prove correct")

Yeah but what if it makes sense for these places to mutate it? For example, I'm making a parser for my language and each different statement will be parsed from a function that will be in a different file. It makes sense for those files to be able to mutate some global variables.

>
  1. globals are singletons, it becomes difficult to have multiple instance if you later regret having only one instance.

  2. because you need to use locking correctly to prevent multiple threads from mutating the state at once and then you have to prove that your locking strategy is does not lead to starvation or deadlocks.

I don't know about most of this terms and I know only a little about multi-threading programming. So thank you, I will make my research and this info will come in handy!

>

I don't understand what you are trying to say here.

The global variable should be local to the object file. The accessor functions can be globally accessible.

Actually the example I made with the parser will explain that too. I want some specific files to be able to access the global variables. If they are only local to a file then they don't help me a lot and I still have to use pointers to modify that specific data.

>

Passing one pointer isn't particularly expensive. What (non-TLS) globals save you on some CPUs are registers and time spent on allocation.

Thanks! Why talking about allocations tho? We will not need to allocate any memory to pass a pointer to a function and then deference it. Of course if you think that you will push it to the stuck in your main function and then you will call all the other functions in the top.

>

For instance, using globals can make sense in embedded programming on tiny CPUs with very little RAM.

Well yeah! However, embedded programming is an advanced topic so I suppose different rules apply there anyways...

>

https://dlang.org/spec/attribute.html#gshared

Thanks a lot!!

January 26, 2022

On Tuesday, 25 January 2022 at 12:14:39 UTC, Dennis wrote:

>

Global State: a Tale of Two Bad C APIs

Thanks for the great article! This still brings it down to libraries. So I think I understand why. The example are great too!

January 26, 2022

On Tuesday, 25 January 2022 at 12:56:00 UTC, max haughton wrote:

>

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.

Oh, thanks for the explanation! No, the code is not small, it is a bunch of functions that access them actually. What do you mean with "member function". In a class?

January 26, 2022

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

>

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();
}
    ```

Thank you! The thing is that this will not work on me as I want global variables that I will actually be able to mutate.

January 26, 2022

On Tuesday, 25 January 2022 at 14:28:24 UTC, H. S. Teoh wrote:

>

[ The problem with global variables ... it makes debugging an extremely unpleasant experience. ]

Yeah, it makes sense. You need to be sure that your functions will be called in a specific order hence why this can be dangerous in libraries.

>

[ Another problem with global variables is that ... just pass the 2nd connection and you're done. ]

Yeah, you are right and this is why I asked for examples because I haven't worked with everything and I just don't know. I will also have a problem with my project when I add multi-thread support so I will change it's structure now that it is smaller.

>

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.

This actually makes total sense. I will change my program to do that instead! Thanks!

>

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.

Actually, I was wondering something. Maybe I should read the whole reference first but I'll just ask in case you know. Is there a way to just declare a struct/class method and define it outside the struct/class like in C++? It will be very annoying to have all my functions be in the same file. The first method seems better to me.

>

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

Yeah, I didn't thought about that before. That's why I'm glad people always try to help! Thanks a lot for your time!