On 04/11/2012 17:05, Chris Cain wrote:
On Sunday, 4 November 2012 at 14:59:24 UTC, Faux Amis wrote:
I failed to mention that I am mostly talking about private module
scope variables. I don't see how private module scoped vars make for
less testable, readable or more bug prone code.

It's not like I feel that you should never use them, but what he says is
right. The more open access that is given to a variable, the more
difficult it is for the programmer to know what will access or change
it. That becomes a huge problem in many languages that don't use
thread-local variables by default, but it's still a problem in D.

Without using some sort of automated search, you can't know where in the
module a variable is accessed or changed. Sometimes even a search will
be insufficient:
---
module a;
int b; // the variable we're interested in
int c;

void blah() {
    foo();
    bar();
}

void foo() {
    c = b + 1;
}

void bar() {
    b *= 3;
}
---

So, via search, we can see that b is being accessed in foo, and being
changed in bar. However, a simple search will not tell us that blah is
accessing and changing b. Furthermore, any function that uses blah will
also be accessing and changing b. The reason this is a problem is that
it's "hidden" from the people reading the code. You might not know that
using "blah" will write and read from b, which might effect the behavior
of the call to "bax" later on in your code.

Consider this code, however:

---
void main() {
     int b = 5; // the variable we're interested in
     int c = blah(b);
     // What's the value of b here? How about c?
     bax(b);
     flax();
     bongo();
     for(d; sheep(c))
        blast(d);
     // Do you still know what b is? Of course you do!
}

int blah(ref int b) {
     int c = foo(b);
     bar(b);
     return c;
}

void foo(int b) {
     return b + 1;
}

void bar(ref int b) {
    b *= 3;
}

void bax(int b) {
    // large switch statement on b for behaviors
}
---

Now we're explicit in what uses b. All of a sudden, we can reason much
more about the code without doing nearly as much searching. We know who
depends on the value of b and who might change b. If we allowed it to be
a global variable, or a module variable, or even a static struct
variable, we might not always have such a good grasp on what is
happening to it.

And if you don't have a good grasp on what's happening to the state of
your program, you might introduce bugs. Hence, it's bug prone to use
module scoped variables.

As for testability: if the behavior of your code depends on globals
(and/or module-scoped variables), then it should be obvious why it's
more difficult to test. Tests shouldn't be effected by tests run before
nor should they have an effect on tests run after. When you use globals,
your code will violate that by definition. Unless, of course, you spend
time being very careful to reset globals before and/or after each test.
That certainly makes testing more difficult and error prone, though.


Now, that all said, it's not like a velociraptor will jump through your
window and eat your face off if you use a global or a module scoped
variable. I'm sure you can come up with examples of where it might be
beneficial or preferable. But it's something that should be avoided
where you can.

Here's a good resource on why global variables are bad:
http://c2.com/cgi/wiki?GlobalVariablesAreBad

Most of those reasons are applicable to module-scoped variables as well.
Even struct static variables can still be problematic for some of the
same reasons.

In your last paragraph you are getting to my point in my other post:
I think there is nothing wrong with a module scope private var as in D a module is the first encapsulation and adding a wrapper only adds noise.

These are equivalent(from a good-coding pov):
---
module a;

private int i;
---
module b;

// annoying wrapper
//which makes it difficult to have a single b in the program
struct S{
 int i;
}
---

These are also equivalent:
---
module a;

int i;
---
module b;

struct S{
 static int i;
}
---

Reply via email to