Thoughts on static import

Eric has a post on why he thinks static import is bad and has garnered a lot of feedback so far.  I was reading his post, and while I tend to agree that static import is bad, I do so for some slightly different reasons.  One thing I agree on is that static import makes maintainability very difficult.  He says:

“One can argue that for its intended use - things like Math.Sin(), it will make things clearer, which I agree with. The problem is that I can see this feature being used by people who are familiar with their code to save time, and for them it's obvious that they've done that, but that's not true for the poor guy who inherits the code.”

in the feedback darren says:

Darren says “my test of readability = "how long would it take someone who spoke English, but has never seen a computer in their life, to understand the code"“

Wow, this is a very good case to consider then.  Pretend we allowed static import.  Then someone would have written in the code “Sin(blah)”.  When the next person comes along and sees that they go “Oh dear god!! My code is sinning!! What the heck does that mean!?!!”, *Crosses themselves*, and performs an exorcism.  If it were written Math.Sin(blah), then they could say “hrmm... it's sinning, but I guess that's some weird thing that Math geeks do.  Must be a different time of sinning than I'm used to.”  In this case the forcing of explicitly naming something helps in this case because it means that someone who knows English, but isn't a math/computer geek, can have a better chance understanding the code.  Namespaces and types serve to add information that is lacking in just a simple function name.  English is incredibly vague and ambiguous.  (Hell, just imagine if these paragraphs constituted a program.  Would you really understand what it all meant?).  In order to make things clear we move away form english into a more rigorous system specifically sothat someone can come along and, with a small bit of learning (a key differentiating factor with what darren wants), understand what's happening. 

Darren also mentions that this is a godsend when dealing with situations like adding a specialized function to deal with strings: “For example, I HATE duplicated code. It really offends me to see if (instr(theString, searchString)>-1)... I would love to see if (theString.Contains( searchString)) - but as I have no way of adding Contains to String,“

I agree with him on this.  It's incredibly obnoxious to not be able to add this sort of functionality to the appropriate class thus forcing you to put it on another object.  However, that seems to me to be a consequence of the bad design of some classes in the framework and the inability to extend them usefully.  IMO two wrongs don't' make a right.  We should fix those classes rather than adding features do work around bad designs.  What's worse is that by added these features to the language I feel that people will start making more and more of these badly designed classes expecting people to be able to work around the issues with things like static import.

Darren then bring up two more interesting points: “The more information you hide, the stronger the system. I DON'T WANT people caring about (or knowing?) WHERE the functionality is coming from - I just want them to know it's out there, and to use it.” and “Overloading: Now, if blah happens to start of life as an int - then the function above is fine, and Min is resolving to Math.Min. Suppose we then want to change it to type "ReallyBigInt"... well - Math doesn't define a Min of ReallyBigInt. We always have to define a Min for ReallyBigInt's, but why do we have to also change every function using it? I just want to USE my new ReallyBigMath library, and leave everything else exactly the same“

These are excellent points.  However, again, they speak to the poor design of the current frameworks.  We already have a mechanism for hiding information and implementation details and for dealing with overloading.  We have a fricking OO language here (with lasers!! ) and yet we're not using it appropriately.  Going back to both the Math.Sin and Math.Min examples.  Rather than saying Sin(x) or Min(x, y) we should have done: x.Sin (after all the sine is just a property of a number) and x.Minimum(y).  By having actual methods that operate on the instances you have you get rid of th need for static importing of a type and you can easily replace that type with another type in the future and have all your code still work (ahhh, the joy of interfaces).  Look at StringContains.  I would much rather just have string.Contains instead.  that would allow me to say: if (title.Contains(“Mr.“))  instead of “if (StringContains(title, “Mr.“))“, but maybe that's just me.

IMO, this is one of those features that will just breed more bad code.  :-)

I've heard the argument that we shouldn't limit the power one has just because people can abuse a feature.  However, so far I disagree with that.  Look at the Office APIs.  They're extremely confusing, clunky, and badly designed because they were catering to a language feature that allowed users to abuse things badly.  It's been my experience (especially in the C++ world) that features will get abused, badly.  I think, therefor, that it's a bad idea to introduce a feature that will only be a good thing 1-2% of the time, especially when it makes things such a confusing mess when abused.

Note: This is not because I'm a control freak (ok I am... nO staTic impr0t 4 j00!!!!), it's because I don't see any features in the language as free.  Adding something like this in means now everyone picking up C# 2.0 has something new to learn and understand.  Eric said it quite well with “int x = Process(s);  ...  it's a line of code that's easy to understand, because you know what Process is, and more importantly, you know where to go look for it.”  Now every single C# developer will have to take that rule which they've lived by and understood, throw it out the window and retrain their minds to say “I am now no longer invoking a method in the context of the current type, but in the context of all my usings as well.”   We can't just say “if we add this feature people can ignore it and go about their merry way.”

Also, I find this topic fascinating because I prefer functional langauges where it's quite natural to have functions floating around that are accessible without any of the clutter we are forcing here (specifically being inside a class, being static, and needing full name qualification).  However, I still consider the last point about complexity to be valid here.  While there's something to be said for merging OO and functional principles and designs so that you get the best of both worlds, you also end up with a much more complex system that can be a lot tougher to learn.  I could very well be wrong on this.  However, I have always preferred langauge that kept things simple and kept the number of constructs to learn as small as possible.  There is a great sense of knowing (in those languages) that you really do understand anything that you read.  Contrast that with Perl, which is (For me) still incredibly difficul to read and undertand purely because there are so many constructs (And related interactions) to learn and understand.  This is a source of its great power and flexibility, and probably why people love it so much.  However, it's also a reason that many consider it to be an incredible pain to work with.  Remember that many developers don't work in a vacuum.  They work in pairs or on teams, or they work on code they inherited from someone else.  Keeping complexity down is very helpful for making that a much smoother process.  Note: I am showing my bias here.  Darren said “how long would it take someone who spoke english, but has never seen a computer in their life, to understand the code“.  That's not a principle i live by.  With that kind of philosophy I feel that we would end up with programs that read like the current US tax code :-)    

So, in short, I believe that static import is bad because we're using it to get around badly designed APIs (much like optional parameters), and instead we should be fixing up the APIs.  The more “power” features we add that have the potential for abuse, the more I feel that we'll see badly designed APIs that force us to use those features.  It's a vicious cycle that I don't want to start down!