But for how wonderful it is, there are still lots of great things about some of the other language paradigms out there, and I definitely have a bit of language envy for some of their features. I'll list some of these features, in context with why I think they're useful. And though I'm not a language designer, I'll also mention how I could see C# accomplishing some of this. Eric Lippert eat your heart out!
Dynamic Envy:
Constructor Stubbing
I'm a big believer in TDD, which requires object mocking. But mocking in a static language can be annoying because it requires:
- Defining an interface for every class that must be mocked
- Injecting an instance of the interface into the objects that use them
This is why IoC is so popular. And while this is annoying (I've written enough about this in the past), it works. One of the most painful things about this, at least for me, is that you can't call a constructor, which precludes simple code like: var thing = new Thing(otherThing); Instead, a factory class has to be introduced: IThingFactory.New(otherThing) : IThing. And you inject an instance of IThingFactory into the class that wanted to call Thing's constructor. UGH!
But in a dynamic language, none of this is necessary. Anything can be mocked, and the most paradigm shifting example of this is mocking the constructor of a class so it returns a stub!
MyClass.Stub(:new) {...}
Something like this can be accomplished in .NET through IL re-writing, as seen in the new Fakes (previously Moles) from MS in what they call Shims. But I haven't used this yet, because when it was Moles, it required the code to be run in an "instrumented" process, which couldn't be accomplished with the NUnit GUI. I think this is still true in Fakes. But I hope this Fakes framework keeps getting some attention, because this is just what I've always wanted: To be able to tell my runtime, "Instead of the class name "MyClass" actually meaning "MyClass", I want it to mean "MyStubClass" for the duration of this test."
Partial "Compilation"
Sometimes when doing TDD or refactoring you'll want to change the public API of a class. Maybe by changing a method name, or the parameters a method takes, etc. If you have many calls to that method, this will cause lots of compiler errors in a static language. And this makes it impossible to TDD out the API change without fixing all the calls first
Dynamic languages don't suffer from this problem because they don't have a compilation step. Only the files you load at execution time are interpreted, and only the lines that are actually executed will fail due to api signature issues. This can be a blessing or a curse, but in the example I gave above it's a blessing.
I would love to be able to tell my compiler, "I'm only going to be executing the tests in this one file, so just compile that file and it's dependencies and leave everything else alone, K? Thx!"
Not only would that allow me to update my APIs calls one at a time, running tests along the way, but it might also speed up the code -> compile -> test loop!
Sentinel Values
Gary Bernhardt used this technique in the Sucks/Rocks series in Destroy All Software. It's a technique for dealing with null, but it's not the same as the Null Object pattern.
As an example, lets say you were implementing a solution to the Word Ladder problem. What should the "Ladder FindLadder(string startword, string endword)" method return when it doesn't find a ladder? In C#, it would return null. The only allowable values for an object of type "Ladder" are a Ladder instance or null.
Since a dynamic language infers types at runtime, the method doesn't have a typed return value, so you can return anything you want. The Sentinel Value technique takes advantage of this and instead of returning nil, it returns an instance of a NoLadder class. NoLadder is an empty class with no methods or fields or properties. How is this different than returning nil? It's different in the exception you'll get. Instead of "NoMethodError: undefined method `first' for nil:NilClass" you'll get "NoMethodError: undefined method `first' for #<noladder:0x000001010652f0>".
That's awesome! It says right there that your problem is you're holding a NoLadder. And NoLadder only comes from one place in your code, so you know exactly and immediately what the problem is. Contrast that with a null reference exception, which could come from anywhere.
In a static language we can approximate this with the Null Object pattern by creating a Ladder singleton instance called NoLadder. But this is not the same thing. The Null Object pattern usually returns an instance which wouldn't cause an exception, but instead would do nothing. Personally, I've always found this a bit confusing and scary, especially if the object returned would normally have behavior. The other major difference, is there may be times where the sentinel is very specific to a given function, and defining a null object on your class for just one little function isn't very cohesive.
In C#, null is not an object like it is in Ruby. But if it WAS, maybe we could do something like:
public class NoLadder : Null { }Then we could return "new NoLadder()" in place of null. Crazy I know, but the ability to put a name on null would be huge!
Metaprogramming
Just look at ActiveRecord and you'll see the amazing power of Metaprogramming in Ruby. C# has reflection, but it can't even come close to the ability to generate types. Ruby style metaprogramming can't exist in a static language, so I think what I really want instead is Macros.
Or if not full fledged Macros, then F#'s type providers. If you haven't seen these yet, you should totally take a look. They're amazing! They generate types at compile time. That may not seem too exciting, until you see the IDE integration... They generate types AS YOU TYPE! It's very cool, and you never need to re-generate a generated code file or anything, it's totally seamless. Like ActiveRecord, but with compile time types!
Functional Envy:
No Nulls
What's the most common exception you encounter in a static language like C# or Java? NullReferenceException.
F# does have null, but only for interoperating with .NET. F# code itself doesn't allow null, values must have a value at all times. This is accomplished with option types, which are the same as .NET's Nullable
I should point out that this is a much more strict form of the Sentinel Values mentioned above. Sentinel Values still blow up when you hit them, they just make it easy to understand why. No Null prevents the blowup entirely. Between the two, I think I'd go for No Nulls because it completely eliminates an entire class of programmer error. However, the cost is some more syntax noise. Nullables arn't pretty:
var ladder = FindLadder("nice", "mile"); if (ladder.HasValue) PrintLadder(ladder.Value); else PrintNoLadder();Sentinels would be "nicer" and "more elegant" and more "aesthetically pleasing" in cases where the null is a rare degenerate case.
Also worth noting is that while the compiler doesn't exactly have that "no nulls" switch I mentioned, you can approximate this with Code Contracts and static checking. I haven't tried this yet, but it looks pretty useful and it's on my list.
Immutability
After NullReferenceException, I'd wager the next most common programmer error stems from side effects: "How did the value of this variable change?!" As I've been studying and practicing functional programming I've been stunned by how ingrained mutable side-effect programming is in my head. Simply put, I think that way, and thinking any other way is very difficult.
I'm not convinced yet that immutability is better for all problems in all places, but I am convinced that it's a less bug prone way to develop. So I wish C# had widely used immutable data structures.
Of course, what this really means is you're writing recursion instead of loops. I find recursion to be more declarative, especially when combined with pattern matching, which I like. But I also find it requires more thinking to understand, which makes it "harder".
Pattern Matching
I don't think I really need to say much here. Pattern Matching is just completely and totally awesome. It requires dramatically less code, is much easier to understand and read, and just generally rules. However, it does require more advanced language-integrated data structures.
Advanced Integrated Data Structures
Can we get a freaking dictionary syntax?!
Admittedly, F# doesn't have one either, but F# DOES have a beautiful list and tuple syntax, which can be used to easily get you a map:
let d = [1, 'a'; 2, 'b'; 3, 'c'; 4, 'd'] let m = Map.ofSeq dI desperately wish I had nice syntax for basic data types like this. This is one of the things I love about powershell too! And when you have pattern matching that also understands this syntax, that's an amazingly expressive and powerful mix!
So there's a sampling of some of my language envy. I'm sure there are others I forgot to include, and I bet you have yours too, so leave 'em in the comments, or on your blog, or tweet at me!
By dictionary syntax, do you mean something like this?
ReplyDeletevar dict = new Dictionary {
{ "a", 10 },
{ "b", 30 }
};
Because that works today.
Dictionary Initializers: http://msdn.microsoft.com/en-us/library/bb531208.aspx
ReplyDeleteYou left out the type signature:
var dict = new Dictionary { {"a", 10}, {"b", 30} };
It's kinda verbose, no?