www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Why people dislike global variables so much while I find them so

reply rempas <rempas tutanota.com> writes:
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. 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. 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.

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.
Jan 25 2022
next sibling parent reply bauss <jj_1337 live.dk> writes:
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. 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. 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.

 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.
The problem with globals is that anyone and anything can modify it. It becomes especially problematic when your program receives input from elsewhere such as connections like sockets, http etc. or even other threads. In my opinion globals are fine IFF they're only initialized once (and only one place is allowed to modify them) and/or they're immutable Globals are extremely convenient to use and that's also why they're extremely dangerous to use.
Jan 25 2022
parent rempas <rempas tutanota.com> writes:
On Tuesday, 25 January 2022 at 10:18:50 UTC, bauss wrote:
 The problem with globals is that anyone and anything can modify 
 it.

 It becomes especially problematic when your program receives 
 input from elsewhere such as connections like sockets, http 
 etc. or even other threads.

 In my opinion globals are fine IFF they're only initialized 
 once (and only one place is allowed to modify them) and/or 
 they're immutable

 Globals are extremely convenient to use and that's also why 
 they're extremely dangerous to use.
This will be a big problem with libraries but I don't see it been a big problem with non-library type applications. At least if I understand correctly what you are saying (which I probably don't).
Jan 25 2022
prev sibling next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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.
Harder to debug. Harder to reason about as your program grows in size. Harder to scale your program to higher degree of concurrency. But there is a place and room for everything. If you use globals you should keep them local to one object-file and limit access to accessor functions to make it easier to debug your code. If you only use globals because you want to have fewer parameters then the common approach is to instead use a context-object and pass that as single parameter. Unfortunately, D made the mistake of making thread-local the default, and claimed this was a great improvement. Thread local globals are basically something you only want to use in runtime and framework "kernel" like code. That gets you an additional layer of issues, including performance. If you use globals, make sure you either mark them appropriately so that you don't get thread local globals.
Jan 25 2022
parent reply rempas <rempas tutanota.com> writes:
On Tuesday, 25 January 2022 at 10:22:17 UTC, Ola Fosheim Grøstad 
wrote:
 Harder to debug
How?
 Harder to reason about as your program grows
What does this mean? No, I'm not joking, I truly don't get it. Do you mean "reason" as the reason they exist and what they do?
 Harder to scale your program to higher degree of concurrency
Why? What's the difference between having a variable in a global space vs passing a pointer and having to difference it when it comes to concurrency? In both cases, you need to check if what you are trying to use is already in use (unless you don't care, lol!).
 If you use globals you should keep them local to one 
 object-file and limit access to accessor functions to make it 
 easier to debug your code.
I mean, if you keep them local to an object file that they lose their bigger advantage. How many functions will be in this file compared to the whole project? So we don't win much.
 If you only use globals because you want to have fewer 
 parameters then the common approach is to instead use a 
 context-object and pass that as single parameter.
Yeah but in that case, I will have to create an object and use pointers. And also use a pointer for this object and have to dereference it every time. So yeah, the runtime performance will not be happy about that...
 Unfortunately, D made the mistake of making thread-local the 
 default, and claimed this was a great improvement. Thread local 
 globals are basically something you only want to use in runtime 
 and framework "kernel" like code. That gets you an additional 
 layer of issues, including performance.

 If you use globals, make sure you either mark them 
 appropriately so that you don't get thread local globals.
How can I do that?
Jan 25 2022
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Tuesday, 25 January 2022 at 11:37:54 UTC, rempas wrote:
 On Tuesday, 25 January 2022 at 10:22:17 UTC, Ola Fosheim 
 Grøstad wrote:
 Harder to debug
How?
 Harder to reason about as your program grows
Because the program is larger, there are more locations that could mutate the global state. ("reason" ≈ "prove correct")
 Harder to scale your program to higher degree of concurrency
Why? What's the difference between having a variable in a
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 mean, if you keep them local to an object file that they lose 
 their bigger advantage. How many functions will be in this file 
 compared to the whole project? So we don't win much.
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.
 Yeah but in that case, I will have to create an object and use 
 pointers. And also use a pointer for this object and have to 
 dereference it every time. So yeah, the runtime performance 
 will not be happy about that...
Passing one pointer isn't particularly expensive. What (non-TLS) globals save you on some CPUs are registers and time spent on allocation. For instance, using globals can make sense in embedded programming on tiny CPUs with very little RAM.
 If you use globals, make sure you either mark them 
 appropriately so that you don't get thread local globals.
How can I do that?
https://dlang.org/spec/attribute.html#gshared
Jan 25 2022
parent reply rempas <rempas tutanota.com> writes:
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!!
Jan 25 2022
parent reply bauss <jj_1337 live.dk> writes:
On Wednesday, 26 January 2022 at 07:51:30 UTC, rempas wrote:
 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!!
For a compiler, I'd think each step shouldn't modify state but rather you pass the current state to the next step. Ex. first you parse lexical information and you pass that information to the semantic analysis, which then passes that information on etc. You should only "modify" the current localized state IMHO. Ex. the current statement being parsed can and should be modified, but you shouldn't be able to modify anything else like suddenly modifying the compiler configurations in the middle of semantic analysis etc. I think it's especially important for compilers to not use global state because you want to be able to localize every single issue right away to a specific unit in the compiler.
Jan 26 2022
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Wednesday, 26 January 2022 at 08:49:02 UTC, bauss wrote:
 I think it's especially important for compilers to not use 
 global state because you want to be able to localize every 
 single issue right away to a specific unit in the compiler.
I would think that you sometimes can gain a bit of performance by making the symbol table global (as you don't have to rebuild it for every source file), but it can become rather large so there is a trade-off.
Jan 26 2022
parent bauss <jj_1337 live.dk> writes:
On Wednesday, 26 January 2022 at 09:04:27 UTC, Ola Fosheim 
Grøstad wrote:
 On Wednesday, 26 January 2022 at 08:49:02 UTC, bauss wrote:
 I think it's especially important for compilers to not use 
 global state because you want to be able to localize every 
 single issue right away to a specific unit in the compiler.
I would think that you sometimes can gain a bit of performance by making the symbol table global (as you don't have to rebuild it for every source file), but it can become rather large so there is a trade-off.
Well you could pass the symbol table to each step rather than each step accessing the symbol table themselves. But of course it all depends on your situation and what your goals are. It makes sense to use globals in some cases and in others it don't. I think the problem with globals is just how easy you can screw up using them, but when used right they can be useful.
Jan 26 2022
prev sibling next sibling parent reply max haughton <maxhaton gmail.com> writes:
On Tuesday, 25 January 2022 at 09:53:25 UTC, rempas 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?
Jan 25 2022
parent reply rempas <rempas tutanota.com> writes:
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.
Jan 25 2022
parent reply max haughton <maxhaton gmail.com> writes:
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.
Jan 25 2022
parent reply rempas <rempas tutanota.com> writes:
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?
Jan 26 2022
parent reply max haughton <maxhaton gmail.com> writes:
On Wednesday, 26 January 2022 at 08:02:33 UTC, rempas wrote:
 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?
If a class is reasonable, use a class, however if it's just some mutable state then use a struct since you don't need OOP
Jan 26 2022
parent rempas <rempas tutanota.com> writes:
On Wednesday, 26 January 2022 at 15:10:44 UTC, max haughton wrote:
 If a class is reasonable, use a class, however if it's just 
 some mutable state then use a struct since you don't need OOP
Yeah, I changed my code to use a struct. I don't need to allocate memory on the heap and I use "betterC" so I cannot use classes anyway. Thanks!
Jan 26 2022
prev sibling next sibling parent reply forkit <forkit gmail.com> writes:
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.
If a computational path requires the use of a global variable, then use it. What will mess you up, is not the global variable, but whether you know - for certain - the computational path(s) that use that global, and what effect those paths may have on that global. It's pretty straight forward ;-)
Jan 25 2022
parent rempas <rempas tutanota.com> writes:
On Tuesday, 25 January 2022 at 10:54:59 UTC, forkit wrote:
 If a computational path requires the use of a global variable, 
 then use it.

 What will mess you up, is not the global variable, but whether 
 you know - for certain - the computational path(s) that use 
 that global, and what effect those paths may have on that 
 global.

 It's pretty straight forward ;-)
Thanks! Actually, that's exactly how I see it. I only want to use them when necessary. Also it seems that they can be dangerous only when used in libraries and when other people can modify them (when they shouldn't) but I was mostly talking about applications...
Jan 25 2022
prev sibling next sibling parent reply Dennis <dkorpel gmail.com> writes:
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](https://nullprogram.com/blog/2014/10/12/)
Jan 25 2022
parent rempas <rempas tutanota.com> writes:
On Tuesday, 25 January 2022 at 12:14:39 UTC, Dennis wrote:
 [Global State: a Tale of Two Bad C 
 APIs](https://nullprogram.com/blog/2014/10/12/)
Thanks for the great article! This still brings it down to libraries. So I think I understand why. The example are great too!
Jan 25 2022
prev sibling next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
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(); } ```
Jan 25 2022
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 25 January 2022 at 14:20:28 UTC, jmh530 wrote:
 
 [snip]
 * For instance:
 
The star should be associated with this...
Jan 25 2022
prev sibling parent reply rempas <rempas tutanota.com> writes:
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.
Jan 26 2022
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 26 January 2022 at 08:07:10 UTC, rempas wrote:
 [snip]

 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.
I have the same worries about global mutables as others...
Jan 26 2022
parent rempas <rempas tutanota.com> writes:
On Wednesday, 26 January 2022 at 14:16:57 UTC, jmh530 wrote:
 I have the same worries about global mutables as others...
I understand. You must be very careful with them and they should be used only in specific cases.
Jan 26 2022
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
Jan 25 2022
parent reply rempas <rempas tutanota.com> writes:
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!
Jan 26 2022
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jan 26, 2022 at 08:46:04AM +0000, rempas via Digitalmars-d wrote:
 On Tuesday, 25 January 2022 at 14:28:24 UTC, H. S. Teoh wrote:
[...]
 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.
[...] Thanks to UFCS, you don't need to define every function that operates on the struct/class as a method. Free functions work just fine. I.e., instead of this: struct MyData { int x; void method() { x++; } } MyData data; data.method(); you could also write this: struct MyData { int x; } void method(ref MyData mydata) { mydata.x++; } MyData data; data.method(); T -- Debian GNU/Linux: Cray on your desktop.
Jan 26 2022
parent rempas <rempas tutanota.com> writes:
On Wednesday, 26 January 2022 at 15:36:52 UTC, H. S. Teoh wrote:
 Thanks to UFCS, you don't need to define every function that 
 operates on the struct/class as a method. Free functions work 
 just fine.

 I.e., instead of this:

 	struct MyData {
 		int x;
 		void method() {
 			x++;
 		}
 	}

 	MyData data;
 	data.method();

 you could also write this:

 	struct MyData {
 		int x;
 	}

 	void method(ref MyData mydata) {
 		mydata.x++;
 	}

 	MyData data;
 	data.method();


 T
That's cool! Thanks!
Jan 27 2022
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jan 25 2022
parent reply rempas <rempas tutanota.com> writes:
On Tuesday, 25 January 2022 at 14:29:32 UTC, Steven Schveighoffer 
wrote:
 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.
Yeah, I understand know how this can be very important with bigger and more complex apps where a lot of developers will work or with Library APIs.
 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.
What do you mean "properties"? I suppose not struct/class properties right?
 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).
That's actually a really great way of doing it but it will be a mess if you have the huge functions that I do. This will work great with small algorithms. Or you could use string enums to insert code in place. A lot of things to consider!
 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
This is what I'll probably do. Not only because of the problem you mentioned but because it will allow my to use functions in separate files like I normally would! Thanks a lot for your time man!
Jan 26 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/26/22 4:10 AM, rempas wrote:
 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.
What do you mean "properties"? I suppose not struct/class properties right?
A property is a functional setter/getter (or only one of those) that prevents incorrect usage. For example, if you have a global int, and it really should only be in the range of 1 to 1000, you can guard the setting of the int to make sure that value was correct. Or if you have a class reference stored globally, you may want to prevent someone from reassigning the class reference to another object. Then you can restrict access to getting only, and not allow setting. You can implement a property in D at global level, e.g.: ```d private Resource _resource; public Resource resource() { return _resource; } // only can access via this property ``` -Steve
Jan 26 2022
parent rempas <rempas tutanota.com> writes:
On Wednesday, 26 January 2022 at 16:04:36 UTC, Steven 
Schveighoffer wrote:
 A property is a functional setter/getter (or only one of those) 
 that prevents incorrect usage.

 For example, if you have a global int, and it really should 
 only be in the range of 1 to 1000, you can guard the setting of 
 the int to make sure that value was correct.

 Or if you have a class reference stored globally, you may want 
 to prevent someone from reassigning the class reference to 
 another object. Then you can restrict access to getting only, 
 and not allow setting.

 You can implement a property in D at global level, e.g.:

 ```d
 private Resource _resource;

 public Resource resource() { return _resource; } // only can 
 access via this property
 ```

 -Steve
Oh so it was a class property like I thought! Thanks for the explanation!
Jan 27 2022
prev sibling next sibling parent reply bachmeier <no spam.net> writes:
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.
Jan 25 2022
parent reply rempas <rempas tutanota.com> writes:
On Tuesday, 25 January 2022 at 14:44:01 UTC, bachmeier wrote:
 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.
Yeah, it turns out that for most of my variables, I should use a struct instead. But there are some variables that I will keep global because I want almost everything to be able to access them and they will be immutable. These variables will also have one instance and this will never change so it makes more sense for them to be 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.
I started getting interested in programing in 2020 (even tho I had some prior experience with Python in school but this doesn't count) so I didn't had the joy to work with any version of FORTRAN. Fun fact: I thought that FORTRAN (do we really need to write it in capital letters?) is an ancient language but I made a research and found out that it is still updated!!!
Jan 26 2022
parent reply bachmeier <no spam.net> writes:
On Wednesday, 26 January 2022 at 09:27:28 UTC, rempas wrote:

 I started getting interested in programing in 2020 (even tho I 
 had some prior experience with Python in school but this 
 doesn't count) so I didn't had the joy to work with any version 
 of FORTRAN. Fun fact: I thought that FORTRAN (do we really need 
 to write it in capital letters?) is an ancient language but I 
 made a research and found out that it is still updated!!!
The numbers behind the name are roughly the year of the standard. FORTRAN 66 and FORTRAN 77 used all caps. Fortran 90 and later dropped the all caps thing. Modern Fortran isn't bad if you're working in the domain for which it was written. I wouldn't hesitate to take it over C, C++, or Rust for that type of work.
Jan 26 2022
parent rempas <rempas tutanota.com> writes:
On Wednesday, 26 January 2022 at 14:44:28 UTC, bachmeier wrote:
 The numbers behind the name are roughly the year of the 
 standard. FORTRAN 66 and FORTRAN 77 used all caps. Fortran 90 
 and later dropped the all caps thing.

 Modern Fortran isn't bad if you're working in the domain for 
 which it was written. I wouldn't hesitate to take it over C, 
 C++, or Rust for that type of work.
That's cool! What was it written for?
Jan 26 2022
prev sibling next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Jan 25 2022
next sibling parent reply Kyle <kyle kyle.kyle> writes:
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.
Jan 25 2022
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
Jan 25 2022
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Jan 25 2022
prev sibling next sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
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.
Jan 25 2022
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Jan 25 2022
prev sibling parent rempas <rempas tutanota.com> writes:
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/ That report mentions "thousands of global variables", all of which I bet started as "what can go wrong?" Ali
As always, thank for the great info Ali! Wish you an amazing day!
Jan 26 2022
prev sibling next sibling parent reply Dukc <ajieskola gmail.com> writes:
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.
Jan 25 2022
parent rempas <rempas tutanota.com> writes:
On Tuesday, 25 January 2022 at 21:34:57 UTC, Dukc wrote:
 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.
Yeah, of course. In your example, it is actually stupid to use a global variable and it doesn't makes sense.
 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.
Yep, like other said! I'm doing that right now! Thanks!
Jan 26 2022
prev sibling next sibling parent reply forkit <forkit gmail.com> writes:
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; } // ---
Jan 25 2022
parent reply rempas <rempas tutanota.com> writes:
On Tuesday, 25 January 2022 at 23:28:36 UTC, forkit wrote:
 // ---

 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;
 }

 // ---
What ".x" returns? Also this example is not very practical. You could do the same if you had passed "x" as a function parameter?
Jan 26 2022
parent reply forkit <forkit gmail.com> writes:
On Wednesday, 26 January 2022 at 11:24:34 UTC, rempas wrote:
 On Tuesday, 25 January 2022 at 23:28:36 UTC, forkit wrote:
 // ---

 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;
 }

 // ---
What ".x" returns? Also this example is not very practical. You could do the same if you had passed "x" as a function parameter?
so you asked for an example where you could slip up using a global variable. well, that was an example. you have a local variable the same name as global variable.. you mistyped... and... boom!
Jan 26 2022
parent reply forkit <forkit gmail.com> writes:
On Wednesday, 26 January 2022 at 11:29:31 UTC, forkit wrote:

oh. what is actually going on in foo is irrelevant of course.

foo does nothing of any relevance.

the point I was making, was about having a global variable with 
the same name as as a local variable, could 'potentially' slip 
you up.
Jan 26 2022
next sibling parent Basile B. <b2.temp gmx.com> writes:
On Wednesday, 26 January 2022 at 11:38:51 UTC, forkit wrote:
 oh. what is actually going on in foo is irrelevant of course.

 foo does nothing of any relevance.

 the point I was making, was about having a global variable with 
 the same name as as a local variable, could 'potentially' slip 
 you up.
What you describe here is actually not specific to globals. The same can happen with aggregate members ```d struct S { int e; function f() {int e;} } ``` but also inheritance. This is more a problem of name hijacking, except that this is officially _not recognized as such_, since there is a way to distinguish (either using the global access operator or the explicit ThisExp. Here is an example for the inheritance taken from DMD codebase and that was encountered, _for real_, 2 years ago while doing a refactoring ```d class A { bool isscope; } class B : A { bool isscope(); // not even related to A.isscope } ``` now remember that getters can be called without parens. Now imagine you want to rename one of the `isscope`.
Jan 26 2022
prev sibling parent rempas <rempas tutanota.com> writes:
On Wednesday, 26 January 2022 at 11:38:51 UTC, forkit wrote:
 On Wednesday, 26 January 2022 at 11:29:31 UTC, forkit wrote:
 so you asked for an example where you could slip up using a 
 global variable.
 well, that was an example.
 you have a local variable the same name as global variable.. 
 you mistyped... and... boom!
 oh. what is actually going on in foo is irrelevant of course.

 foo does nothing of any relevance.

 the point I was making, was about having a global variable with 
 the same name as as a local variable, could 'potentially' slip 
 you up.
Thanks for the example! You are right but I personally would never use global variables this way as it doesn't make sense. The idea is that, the global should be shared across every (or most) function in my project so there is no point to create a local variable with the same name (which probably also means that it does the same thing as your global one) as the global one. In this case, it will be better to use a local variable. It also makes a lot more sense.
Jan 26 2022
prev sibling next sibling parent reply Mark <smarksc gmail.com> writes:
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. 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. 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.

 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.
Some examples of global variables that are frequently used: 1) C has an implicit global variable in all programs - errno. 2) If you are writing `asm` blocks (in C or D), then the processor's flags and registers are obviously global variables that can affect the function's behavior. At least in D the compiler will complain if a function is marked `pure` but has an inner `asm` block; the block has to be annotated explicitly with `pure`. 3) The language runtime might also be considered as one giant global variable. As an example, a function may return successfully on one invocation and fail on another despite being pure and being invoked with the same parameters, just because a memory allocation has failed. Potentially, you could even catch and handle OurOfMemoryErrors (something which you aren't supposed to do, I think) and change function behavior based on that. However, if I'm not mistaken, any issues related to memory allocation aren't typically considered a part of the function's "contract"; otherwise, `pure` would only be possible if ` nogc` is also present.
Jan 26 2022
parent rempas <rempas tutanota.com> writes:
On Wednesday, 26 January 2022 at 11:52:13 UTC, Mark wrote:
 Some examples of global variables that are frequently used:
 1) C has an implicit global variable in all programs - errno.
 2) If you are writing `asm` blocks (in C or D), then the 
 processor's flags and registers are obviously global variables 
 that can affect the function's behavior. At least in D the 
 compiler will complain if a function is marked `pure` but has 
 an inner `asm` block; the block has to be annotated explicitly 
 with `pure`.
 3) The language runtime might also be considered as one giant 
 global variable. As an example, a function may return 
 successfully on one invocation and fail on another despite 
 being pure and being invoked with the same parameters, just 
 because a memory allocation has failed. Potentially, you could 
 even catch and handle OurOfMemoryErrors (something which you 
 aren't supposed to do, I think) and change function behavior 
 based on that. However, if I'm not mistaken, any issues related 
 to memory allocation aren't typically considered a part of the 
 function's "contract"; otherwise, `pure` would only be possible 
 if ` nogc` is also present.
Thanks for the examples! In '3', you could of course pass "-betterC" so there is no garbage collector at all. Also even if there is no garbage collector, you can have a memory allocation and it can fail. Just wanted to note that.
Jan 26 2022
prev sibling next sibling parent reply Bienlein <jeti789 web.de> writes:
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. 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. 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.

 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.
I once worked on a system (written in Java) that was a huge commercial success. The company has a revenue of about €100 million from it. What later turned out should always have been passed on to methods as a parameter was always a global function. Now many person years have been spent on development and countless lines have been written and there is absolutely no way to fix this as the effort in time and money is way to large. For some little programs you might get away with some globals, but not in case of lage systems.
Jan 26 2022
parent rempas <rempas tutanota.com> writes:
On Wednesday, 26 January 2022 at 15:47:06 UTC, Bienlein wrote:
 I once worked on a system (written in Java) that was a huge 
 commercial success. The company has a revenue of about €100 
 million from it. What later turned out should always have been 
 passed on to methods as a parameter was always a global 
 function. Now many person years have been spent on development 
 and countless lines have been written and there is absolutely 
 no way to fix this as the effort in time and money is way to 
 large. For some little programs you might get away with some 
 globals, but not in case of lage systems.
Thanks for the example! I must say that I didn't though about big projects that will be shared across a lot of developers. Thankfully, thanks to all these replays, I have learned my lesson!
Jan 27 2022
prev sibling parent reply MrJay <mrjcraft2021 gmail.com> writes:
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. 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. 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.

 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.
when you create a global variable anything can modify it, meaning that if someone else writes code, or another thread changes the variable, it can cause side affects, and it can be difficult to follow especially because you may not know what or where the bug is, also when you pass it into a function then you are effectively documenting the code, this means in the future it will be much easier to debug because you know where it is going, especially when working in a team environment. however this is yes good practice but I still use global variables when it is convenient for a smaller project where the code fits on one screen, or the name of the variable describes its location. but I work for myself and I am the only one who sees my code. a lot of code practices people recommend such as not using macros is because of team environments' because many convenient code practices can easily cause confusion for your peers. but I still dont understand why people see it as a sin of coding to use global variables, it depends on the code base and the project. In some of my code there is only global variables because one of the languages I use only uses global variables so if something is broken it takes a hot minute, I dont know what function is causing the error, just have to go through them all.
Jan 26 2022
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jan 26, 2022 at 11:13:56PM +0000, MrJay via Digitalmars-d wrote:
[...]
 a lot of code practices people recommend such as not using macros is
 because of team environments' because many convenient code practices
 can easily cause confusion for your peers. but I still dont understand
 why people see it as a sin of coding to use global variables, it
 depends on the code base and the project.
[...] I don't consider it a "sin" to use global variables, I've done that myself in the past (and still do it occasionally for one-off scripts, because let's face it, it's darned convenient). Only thing is, in pretty much every non-trivial project where I've used globals, I ended up regretting it later because of the myriad of problems associated with using global variables. Such as not being able to create new instances without rewriting a ton of code (e.g., the database connection example in my other post), being confusing to debug (some global changed between the call to f() and the call to g(), but it's totally non-obvious from reading the code and left me scratching my head as to why g() is suddenly behaving funny). This applies not only in team environments -- in single-person projects the "confused team member" could very well be yourself after 3 months ("WHAT I was thinking when I wrote this?!"). Or, in my case, 5 years ("WHAT was I trying to do when I wrote this crock?!"). After many experiences of this, I've come to the conclusion that the convenience of using globals is usually not worth the trouble they cause down the road. I rather "waste" a bit more time writing things the "right" way now, than to waste a LOT of time (and frustration) later trying to debug code riddled with global state. T -- It said to install Windows 2000 or better, so I installed Linux instead.
Jan 26 2022
next sibling parent reply forkit <forkit gmail.com> writes:
On Wednesday, 26 January 2022 at 23:40:48 UTC, H. S. Teoh wrote:
 After many experiences of this, I've come to the conclusion 
 that the convenience of using globals is usually not worth the 
 trouble they cause down the road. I rather "waste" a bit more 
 time writing things the "right" way now, than to waste a LOT of 
 time (and frustration) later trying to debug code riddled with 
 global state.


 T
I just counted the globals in my GUI IDE (that I developed with 34 many set deafault, that can later be modified during function calls, depending on needs at the time. Without globals, I'd never have bothered ;-) bool new_compile_needed = true; public bool new_record_mode = false; static string custom_tmp = System.IO.Path.GetTempPath() + "EZ_Compiler\"; string vcvar64 = ""; string vcvar32 = ""; string clang_exe = "clang.exe"; bool Is64Bit; string clang_xp = "C:\LLVM\bin\clang-cl.exe"; static string g_cl_c_options = " /nologo /W4 /GS- /Od"; string g_cl_c_options_new = g_cl_c_options; static string g_cl_cpp_options = " /nologo /EHsc /W4 /GS- /Od"; string g_cl_cpp_options_new = g_cl_cpp_options; static string langversion = " /langversion:default"; static string g_csc_options = " /nologo /warn:4 /warnaserror+ /nowarn:168,164 /checked+" + langversion + " /optimize+ /target:exe"; string g_csc_options_new = g_csc_options; // see compile method... static string g_csc_unsafe_options = " /nologo /warn:4 /warnaserror+ /nowarn:168,164 /checked+" + langversion + " /optimize+ /unsafe /target:exe"; string g_csc_unsafe_options_new = g_csc_unsafe_options; bool vs2019_available = false; bool vs2015_available = false; string exeDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); string current_file = "Current File: (none)"; string dcompiler_mars = "dmd.exe"; string dcompiler_llvm = "ldc2.exe"; string dcompiler = "dmd.exe"; string gocompiler = "go.exe"; string jscompiler = "jrunscript"; string cs_formatter = "CodeFormatter.exe"; string csprojFileName = custom_tmp + "cs" + ".csproj"; string myDLibDirectory = " -i -I=" + '\u0022' + "D:\\My Documents\\Dlib" + '\u0022' + " "; string Cpp64dll = "c:\nowheretobefound"; string Cpp32dll = "c:\nowheretobefound"; string myDocsFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); System.Drawing.Color MyBackColor = Color.Black; System.Drawing.Color MyForeColor = Color.Lavender;
Jan 26 2022
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jan 27, 2022 at 12:45:20AM +0000, forkit via Digitalmars-d wrote:
[...]

 
 34
 
 many set deafault, that can later be modified during function calls,
 depending on needs at the time.
 
 Without globals, I'd never have bothered ;-)
[...] Some of your globals are actually global values, like:
 string clang_exe = "clang.exe";
 bool Is64Bit;
 bool vs2019_available = false;
 bool vs2015_available = false;
 string dcompiler_mars = "dmd.exe";
 string dcompiler_llvm = "ldc2.exe";
These I wouldn't be ashamed of declaring as globals, since they ARE globals (in the sense of describing the global environment the program will run in). You can usually tell these from other globals by whether or not you can make them immutable (though sometimes you may want to keep them mutable if, e.g., you're picking them up from the runtime environment or via program options -- but the idea being that once they're initialized they're effectively immutable). Others, however, I'd stick into a configuration struct or as member fields in your program's main class, such as:
 bool new_compile_needed = true;
 public bool new_record_mode = false;
 System.Drawing.Color MyBackColor = Color.Black;
 System.Drawing.Color MyForeColor = Color.Lavender;
T -- Do not reason with the unreasonable; you lose by definition.
Jan 26 2022
parent max haughton <maxhaton gmail.com> writes:
On Thursday, 27 January 2022 at 01:01:56 UTC, H. S. Teoh wrote:
 On Thu, Jan 27, 2022 at 12:45:20AM +0000, forkit via 
 Digitalmars-d wrote: [...]
 I just counted the globals in my GUI IDE (that I developed 

 
 34
 
 many set deafault, that can later be modified during function 
 calls, depending on needs at the time.
 
 Without globals, I'd never have bothered ;-)
[...] Some of your globals are actually global values, like:
 string clang_exe = "clang.exe";
 bool Is64Bit;
 bool vs2019_available = false;
 bool vs2015_available = false;
 string dcompiler_mars = "dmd.exe";
 string dcompiler_llvm = "ldc2.exe";
These I wouldn't be ashamed of declaring as globals, since they ARE globals (in the sense of describing the global environment the program will run in). You can usually tell these from other globals by whether or not you can make them immutable (though sometimes you may want to keep them mutable if, e.g., you're picking them up from the runtime environment or via program options -- but the idea being that once they're initialized they're effectively immutable). Others, however, I'd stick into a configuration struct or as member fields in your program's main class, such as:
 bool new_compile_needed = true;
 public bool new_record_mode = false;
 System.Drawing.Color MyBackColor = Color.Black;
 System.Drawing.Color MyForeColor = Color.Lavender;
T
The distinction is basically between global state (i.e. things that are mutated often, like "pointer to current item") and globals that are *stated* e.g. "Is logging turned on". The latter is mostly fine with the caveat that it can be indicative of bad architecture (e.g. multiple instances of something like an interpreter is often impossible with any global state at all), the former is extremely bad and can absolutely ruin a program. If you have lots of global state (or even local state whose structure isn't natural) you are doing something wrong.
Jan 26 2022
prev sibling parent rempas <rempas tutanota.com> writes:
On Thursday, 27 January 2022 at 00:45:20 UTC, forkit wrote:
 Without globals, I'd never have bothered ;-)
This may sound stupid but you would never have bothered what? Doing the project?
Jan 27 2022
prev sibling parent rempas <rempas tutanota.com> writes:
On Wednesday, 26 January 2022 at 23:40:48 UTC, H. S. Teoh wrote:
 "WHAT I was thinking when I wrote this?!"
Yep! That's totally me every single time! When I'm writing the code I feel like a genius! But I only understand how much of an idiot I am after I read the same code some months later. We learn and improve I guess...
 "WHAT was I trying to do when I wrote this
Totally me again! Tbh, How many times have you though about solving a problem and then down the road, you thought of a better way to do it that is both more performant and more continent and thought "why did I wrote this code this way? It's completely stupid!"? Yet at the time you wrote the code, it looked perfect! Humans are interesting creatures...
Jan 27 2022
prev sibling parent rempas <rempas tutanota.com> writes:
On Wednesday, 26 January 2022 at 23:13:56 UTC, MrJay wrote:
 a lot of code practices people recommend such as not using 
 macros is because of team environments' because many convenient 
 code practices can easily cause confusion for your peers. but I 
 still dont understand why people see it as a sin of coding to 
 use global variables, it depends on the code base and the 
 project.
Yeah, I thought the same hence why I made this question. Tho from what I've seen, most of people (at least in the D community) use whatever does the job best so global variables can be useful IF used with caution. But I realized that in my case, wrapping my variables in a struct is a better choice cause not only it's more convenient but I will also have a problem when I implement mult-therading support!
 In some of my code there is only global variables because one 
 of the languages I use only uses global variables so if 
 something is broken it takes a hot minute, I dont know what 
 function is causing the error, just have to go through them all.
Yeah, that's very very bad indeed! Thanks a lot for your time! Really appreciate it!
Jan 27 2022