Discussion on using module-scoped variables (was 'with' bug?)

Chris Cain clcain at uncg.edu
Mon Nov 5 22:46:01 PST 2012


On Monday, 5 November 2012 at 08:37:49 UTC, Faux Amis wrote:
> Ok, good to see that you are replying to incorrectly scoped 
> variables, but this is not the point I am trying to make. I 
> know you should always keep the scope as small as possible.

Eh? I'm confused. The second half of my post certainly was a bit 
of a rant on incorrectly scoped variables (which is related to 
the discussion, but it was my response to bearophile), but the 
first part of my post is supporting the viewpoint that you should 
avoid using module-scoped variables (and even static struct 
member variables) and suggesting an alternative.

> Can you think of a setting in which we have legitimate private 
> struct members? If so, then add to this setting that you only 
> want one instantiation of the data in this struct. As a 
> solution, what is wrong with dropping the struct encapsulation? 
> There is no other code except the struct in the module.
>
> I sincerely want to know if there is any difference. As I 
> understand it the scope is exactly the same or even smaller as 
> you can't leak instances of modules as you can structs.

 From my understanding, you're trying to get a specific viewpoint 
on this idea?

> From a good-coding standpoint, do you think there is a 
> difference between these two options?
>
> --
> module a;
>
> int a;
> --
> module b;
>
> struct S{//otherwise unused wrapper
> static int b;
> }
> --

I think the first option is "better" than the second. The second 
seems to be a misuse of struct to me. I can't see why you'd use a 
struct in the second option.

That said, they're effectively equivalent pieces of code. That 
is, they're both static data that is a shared resource among all 
functions that have access to them. And, thus, they're both 
"equally bad" in terms of how they will affect the 
understandability and testability of the code. It's possible that 
it would cause the code to have more bugs in it than it would 
otherwise.

That is, both of them are not as good of choices as this:
---
void good(ref int b) pure {
     // code using/setting b
}
void good2(int b) pure {
     // code using b, but not setting it
}

// No b in module scope or statically allocated

// Sometimes a better idea, depending on circumstances:
int better(int b) pure {
    // code using b, and putting changes into changedB
    return changedB;
}

// example uses:
void main() {
     int a = 1, b = 2, c = 3;
     good(a);
     good2(b);
     c = better(c);
}
---

Because this creates code that is honest about its dependencies 
and allows for the overall state of the program to be consistent 
between runs of functions. This is essential for testability, but 
it's also important for a programmer to reason about the behavior 
of their code.

Of course, I'm sure you can give examples of code that couldn't 
be written like that, and that's okay. I'm only arguing that you 
should avoid static data when it's realistic to do so, not that 
it will open a black hole in your living room if you use it under 
any circumstances :-). Though if you're using it as your "primary 
data encapsulation," I have to wonder whether you're using it in 
instances it could have been avoided.


More information about the Digitalmars-d mailing list