'with' bug?

Chris Cain clcain at uncg.edu
Sun Nov 4 08:05:47 PST 2012


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.


More information about the Digitalmars-d mailing list