std.getopt suggestion

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Thu Sep 29 08:41:33 PDT 2011


On 9/29/11 3:17 AM, Jonathan M Davis wrote:
> On Thursday, September 29, 2011 00:40:44 Andrei Alexandrescu wrote:
>> Why?
>
> 1. Mutable globals are generally considered to be bad practice. As you
> yourself have stated before, Phobos should be an example of good software
> practices in D. Having mutable globals goes directly contrary to that goal.

Globals are bad when functions communicate through them. std.getopt is 
one function, which is overwhelmingly called once. The variables that 
condition its workings are unrestricted. It is poorer style to create an 
object just for the sake of calling a method for it. GetOpt doesn't even 
make sense as a noun. It would be something like OptGetter - ah, the 
stigma of "er"-ending objects.

> 2. Conceptually, what happens with getopt is that you configure it and then run
> it. It is much better encapsulation for the configuration to be tied to the
> function call. The normal way to do this (at least in an OO language) is to
> make it a member function of the configuration. As it stands, those
> configuration variables are just sitting in the same module. As getopt is
> really the only function of consequence in the module, it's not as big a deal
> as it would be in most modules, but it's still better encapsulation to
> actually tie the configuration to the function that it's configuring.

Encapsulation makes sense when there's matter to encapsulate. I don't 
see a point in encapsulating one function that's called once and two 
unrestricted variables.

> 3. By putting the configuration options in a struct, they are better organized.
> It makes it easier to see what the whole list is without having to search the
> module. It also gives a very good place to document them all together.

Not buying this at all. It's a module with one blessed function!

> 4. If you need to run getopt multiple times - particularly if you need to run
> it with different configurations each time - it's definitely cleaner to do that
> when you can just use a different GetOpt instance in each case. You don't have
> to worry about one run of getopt affecting another. Now, this matters far less
> for getopt than it would your average function, since it would be highly
> abnormal to need to run getopt multiple times like that, but the general
> design principle holds.

It would be quite abnormal to run getopt multiple times in the same app 
with different configurations. So in this case using globals is 
_better_, not worse.

> 5. Assuming that we were creating std.getopt from scratch, there would be
> _zero_ benefit in having any of its configuration options be at the module
> level.

If I were creating std.getopt today from scratch, I'd define it the same 
way it is. It is simple, to the point, and gets its job done. It 
compares favorably with all getopt frameworks I've ever seen. I am very 
happy with its design and implementation.

> There is a definite argument for leaving them there given that moving
> them could break code (though honestly, it wouldn't surprise me if no one in
> the history of D has ever written a program that changed any of those
> variables from their defaults given how standard they are and how little gain
> there normally is in changing them), but from the perspective of design, I
> don't see any reason why it would ever be better to have the variables be at
> module scope. On the contrary, it goes against what is generally considered
> good design.

Not buying this at all. I've seen "good" and "better" a lot of times in 
this email, but not properly substantiated for the case at hand. The 
design of a simple artifact should be simple. Introducing an object to 
obey design principles not applicable to the case at hand strikes me as 
futile.

> 6. Putting the configuration in a struct would probably allow getopt to be pure
> (depending on its implementation). I don't know if there would ever be a
> program where that would really matter given how getopt is normally used, but
> it would be a benefit to having the configuration encapsulated in a struct
> rather than at module scope. And as it stands, getopt definitely can't be pure,
> so if it ever did matter, it would be a problem.

This is a good argument.

> Honestly, I think that it primarily comes down to it being better design to
> encapsulate the configuration options together, tying them to the function that
> they're for, rather than having mutable variables at module scope. And Phobos
> should not only be well-designed on general principle, but it's supposed to be
> an example good D code and practices, and as it stands, std.getopt doesn't do
> that with regards to module-level variables. The only reason that I see to
> leave it as it is is because it's already that way.

I see another reason: it works very well and it keeps simple things simple.

> And if we mess with what's currently there in order to change any of the
> defaults for the config enum (as opposed to the variables at module scope which
> we've been discussing) and/or to change getopt to getOpt to follow Phobos'
> naming conventions, it just makes sense to change anything about the design
> which is suboptimal which can be reasonably changed.

The proposed change adds net negative value. It forces people to create 
an object in order to call a simple function, for the vague benefit of 
tenuous corner cases.

I kindly suggest we stop the isometric workout and look into adding good 
value to Phobos.


Thanks,

Andrei


More information about the Digitalmars-d mailing list