digitalmars.D - Why people dislike global variables so much while I find them so
- rempas (20/20) Jan 25 2022 It is known that people dislike global variables and the reason
- bauss (11/32) Jan 25 2022 The problem with globals is that anyone and anything can modify
- rempas (4/14) Jan 25 2022 This will be a big problem with libraries but I don't see it been
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (17/21) Jan 25 2022 Harder to debug. Harder to reason about as your program grows in
- rempas (18/34) Jan 25 2022 How?
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (18/35) Jan 25 2022 Because the program is larger, there are more locations that
- rempas (23/40) Jan 25 2022 Yeah but what if it makes sense for these places to mutate it?
- bauss (14/56) Jan 26 2022 For a compiler, I'd think each step shouldn't modify state but
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/8) Jan 26 2022 I would think that you sometimes can gain a bit of performance by
- bauss (10/18) Jan 26 2022 Well you could pass the symbol table to each step rather than
- max haughton (5/6) Jan 25 2022 Passing state down a huge chain is also a code-smell. Writing
- rempas (3/7) Jan 25 2022 What "rough outline" of a function means? Sorry if that sounds
- max haughton (19/27) Jan 25 2022 What I mean by outline (where global is the thing you don't like
- rempas (4/22) Jan 26 2022 Oh, thanks for the explanation! No, the code is not small, it is
- max haughton (3/27) Jan 26 2022 If a class is reasonable, use a class, however if it's just some
- rempas (4/6) Jan 26 2022 Yeah, I changed my code to use a struct. I don't need to allocate
- forkit (7/11) Jan 25 2022 If a computational path requires the use of a global variable,
- rempas (6/13) Jan 25 2022 Thanks! Actually, that's exactly how I see it. I only want to use
- Dennis (3/5) Jan 25 2022 [Global State: a Tale of Two Bad C
- rempas (3/5) Jan 25 2022 Thanks for the great article! This still brings it down to
- jmh530 (37/43) Jan 25 2022 I just made use of globals in a non-D project at work. Basically,
- jmh530 (2/6) Jan 25 2022 The star should be associated with this...
- rempas (3/41) Jan 26 2022 Thank you! The thing is that this will not work on me as I want
- H. S. Teoh (50/66) Jan 25 2022 The problem with global variables is that firstly, it modifies the
- rempas (18/38) Jan 26 2022 Yeah, it makes sense. You need to be sure that your functions
- H. S. Teoh (26/36) Jan 26 2022 [...]
- rempas (2/24) Jan 27 2022 That's cool! Thanks!
- Steven Schveighoffer (18/24) Jan 25 2022 That's not the reason. Global variables are hard to control and review.
- rempas (15/33) Jan 26 2022 Yeah, I understand know how this can be very important with
- Steven Schveighoffer (16/21) Jan 26 2022 A property is a functional setter/getter (or only one of those) that
- rempas (4/20) Jan 27 2022 Oh so it was a class property like I thought! Thanks for the
- bachmeier (20/22) Jan 25 2022 I object to your question. There's not necessarily anything wrong
- rempas (13/21) Jan 26 2022 Yeah, it turns out that for most of my variables, I should use a
- =?UTF-8?Q?Ali_=c3=87ehreli?= (23/28) Jan 25 2022 The situation is much better in D because I suspect what you call global...
- Kyle (10/12) Jan 25 2022 This is a good read, other than this paragraph:
- H. S. Teoh (21/34) Jan 25 2022 Don't worry, it happens more frequently than most people would like to
- H. S. Teoh (14/21) Jan 25 2022 [...]
- Patrick Schluter (14/40) Jan 25 2022 Toyota, lol. I also had once to interact with software coming
- H. S. Teoh (15/29) Jan 25 2022 And this, ladies and gentlemen, is a prime example of why the range API
- rempas (2/31) Jan 26 2022 As always, thank for the great info Ali! Wish you an amazing day!
- Dukc (22/24) Jan 25 2022 Assuming you mean mutable global state. Immutable global data is
- rempas (4/24) Jan 26 2022 Yeah, of course. In your example, it is actually stupid to use a
- forkit (18/22) Jan 25 2022 // ---
- rempas (3/20) Jan 26 2022 What ".x" returns? Also this example is not very practical. You
- forkit (6/32) Jan 26 2022 so you asked for an example where you could slip up using a
- Mark (20/41) Jan 26 2022 Some examples of global variables that are frequently used:
- rempas (5/24) Jan 26 2022 Thanks for the examples! In '3', you could of course pass
- Bienlein (10/31) Jan 26 2022 I once worked on a system (written in Java) that was a huge
- rempas (4/13) Jan 27 2022 Thanks for the example! I must say that I didn't though about big
- MrJay (22/43) Jan 26 2022 when you create a global variable anything can modify it, meaning
- H. S. Teoh (26/31) Jan 26 2022 [...]
- forkit (49/56) Jan 26 2022 I just counted the globals in my GUI IDE (that I developed with
- H. S. Teoh (16/34) Jan 26 2022 [...]
- max haughton (10/44) Jan 26 2022 The distinction is basically between global state (i.e. things
- rempas (3/4) Jan 27 2022 This may sound stupid but you would never have bothered what?
- rempas (11/13) Jan 27 2022 Yep! That's totally me every single time! When I'm writing the
- rempas (10/20) Jan 27 2022 Yeah, I thought the same hence why I made this question. Tho from
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
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
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
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
On Tuesday, 25 January 2022 at 10:22:17 UTC, Ola Fosheim Grøstad wrote:Harder to debugHow?Harder to reason about as your program growsWhat 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 concurrencyWhy? 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
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:Because the program is larger, there are more locations that could mutate the global state. ("reason" ≈ "prove correct")Harder to debugHow?Harder to reason about as your program grows1. 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.Harder to scale your program to higher degree of concurrencyWhy? What's the difference between having a variable in aI 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.https://dlang.org/spec/attribute.html#gsharedIf 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
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#gsharedThanks a lot!!
Jan 25 2022
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: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.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#gsharedThanks a lot!!
Jan 26 2022
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
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: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.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
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
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
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: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.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
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
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: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 OOPWhat 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
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 OOPYeah, 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
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
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
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
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
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
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
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
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
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
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
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. TYeah, 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
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:[...][...] 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.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.
Jan 26 2022
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(); TThat's cool! Thanks!
Jan 27 2022
On 1/25/22 4:53 AM, rempas wrote:It is known that people dislike global variables and the reason is that they make the code harder to debug.That's not the reason. Global variables are hard to control and review. That is, if a variable is global, how do you know it's not misused? The point of not using global variables is to reduce the surface area of the program that needs review to ensure it's correctly used. In some cases, they make sense, and in those cases, I'd recommend at least guarding the direct access to the variable through properties so you can control how it's used.In my experience tho, it is the exact opposite. When I have a variable that I must pass down to 5-6 functions, I find it much easier to make it global rather than having it been passed in all the functions that need it.In D, I would do this one of 2 ways: 1. Declare the variables in a function, then run your algorithms inside inner functions. They all now have access to the variable. I'm surprised at how many complex problems and APIs become super-straightforward when you start using local functions (even templated ones). 2. Enclose the data and methods in a struct, even if that struct is a struct inside a function. This is a necessity if you have mutually recursive functions, since a local function cannot call another local function that hasn't been defined yet. -Steve
Jan 25 2022
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. -SteveThis 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
On 1/26/22 4:10 AM, rempas 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 ``` -SteveIn 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?
Jan 26 2022
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 ``` -SteveOh so it was a class property like I thought! Thanks for the explanation!
Jan 27 2022
On Tuesday, 25 January 2022 at 09:53:25 UTC, rempas wrote:It is known that people dislike global variables and the reason is that they make the code harder to debug.I object to your question. There's not necessarily anything wrong with global variables, for loops, goto statements, and whatever else some claim you shouldn't use. Maybe you're thinking of Haskell. I don't think it's common that you need global variables in D. If you want to share a variable among several functions, use a struct. But if a global is the best solution, use the global. If you've ever had the joy of working with FORTRAN 77 or earlier versions of FORTRAN (back when it was in all caps) you understand why working with globals can be a traumatic experience. For instance, multiple functions in multiple files will sometimes change the same global variable. You have global variable x. foo calls bar which calls baz which changes x. Then 75 lines later foo calls goo, which also changes x. Even if there's no bug in your code, this style of programming will burn threw all your brain energy in a hurry. You have to think about the entire program in order to reason about one part. It was often easier to rewrite the whole thing from scratch than to make a meaningful change involving global variables.
Jan 25 2022
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
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
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
On 1/25/22 01:53, rempas wrote:people dislike global variablesThe 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 upOtherwise, 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
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
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
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
On Tuesday, 25 January 2022 at 15:47:12 UTC, Ali Çehreli wrote:On 1/25/22 01:53, rempas 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. 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.people dislike global variablesThe 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 ratherthan having itbeen 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 upOtherwise, as everybody else told, global variables can be very dangerous: https://www.safetyresearch.net/toyota-unintended-acceleration-and-the-big-bowl-of-spaghetti-code/
Jan 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
Jan 25 2022
On Tuesday, 25 January 2022 at 15:47:12 UTC, Ali Çehreli wrote:On 1/25/22 01:53, rempas wrote:As always, thank for the great info Ali! Wish you an amazing day!people dislike global variablesThe 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 ratherthan having itbeen 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 upOtherwise, 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 26 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.
Jan 25 2022
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
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
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
On Wednesday, 26 January 2022 at 11:24:34 UTC, rempas wrote:On Tuesday, 25 January 2022 at 23:28:36 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!// --- 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
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
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
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
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
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
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
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
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
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
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. TI 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
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
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: [...]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.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
Jan 26 2022
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
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 thisTotally 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
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