Elegance or Practicality: Pick One
I’m going to start with a story, which is a true story, that turns out to be a useful metaphor for programming languages.
When I was in college, Raymond Smullyan came to present a guest lecture on infinities. It was an entertaining lecture. Smullyan is a fascinating man and very good at explaining the intractable. In particular, take a look at his explanation of Gödel’s Incompleteness theorem. I was pleased to discover that several of my fellow students had grown up reading some of his puzzle books.
Many of the puzzles center on an island of Knights and Knaves. Knights always tell the truth. Knaves always lie. The problem is that you don’t know which is which so it’s hard to get a straight answer to anything. The classic example is coming to a fork in a road where there are two men. One is a knight. One is a knave. You don’t know which is which. You may ask one question to find the right road to town. In thinking it over, you turn to either one and ask the question, “If I ask the other which road to take to town, which would he say?” Then you take the other one.
After talking about this for a while, one of my classmates, Judith, came up with her own version of the problem. You’re at a crossroads and there are two men. One is a knight. One is a knave. You need to find the road which is the shortest distance to town. You’re allowed to ask one question. What is it? Her answer is this: “Did you hear they’re giving out free beer in town?” Then you follow the men into town.
The first puzzle is an elegant little puzzle which requires an elegant metaquestion to get the answer, something that would only work in a world where people lie so simply. The second puzzle is whimsical and has a solution that is highly practical and whimsical too.
And this is where I think language designers can easily fall into traps. In striving for elegance (or consistency) in language design, they often end up losing practicality. In putting practicality into a language, you lose elegance. Pascal was one of those languages that drove me insane in trying to do anything useful.
One big point of pain is that in evaluating boolean expressions, Pascal evaluates the whole expression first, which means that if you wanted to do some typical linked list node checking, you end up with a pile of nested IF’s made worse by the pile of big keywords BEGIN and END if you want to do anything more than a line – but it’s elegant in terms of the language and makes it easier to write the compiler. C clearly made the right choice of short circuiting boolean expressions and single characters to delineate blocks of code.
Similarly, Java has a very elegant way of defining enumerations. Used in the simplest way, it’s just a list of symbols, but it has the ability to encompass multiple values associated with any enumeration. I think it’s more accurate to say that Java’s enumerations are really named compile-time singletons. This is very elegant – there are some neat things you can get out of them. They problem is that if you need to interoperate with other systems that use particular constants, you start encountering pain. Now you have to write a constructor for the enum and call it from the declaration of each symbol. Fine. Whatever – but I can cast it to the underlying type or I get a sugared accessor, right? No – you want an accessor, you have to write it. You want reverse mapping, you have to write it. Fine, fine. Ok, suppose I want to do bit fields? You are encouraged to use an EnumSet<T> instead of integer constants. Fine, fine, that’s a very elegant solution (albeit sparse in its implementation) – but what about when I need to go to actual hardware and I need that set as an int? Pain. And more pain.
Compare that with C#’s enumerations. They are admittedly sparse in comparison to Java’s in terms of capabilities, but quite honestly, nearly all the practical things I needed to do with C# enumerations were straightforward and painless by comparison. The things that didn’t fit turned out to be a poor fit for enumerations anyway. Sure, the [Flags] attribute is a bit of a hack, but one I gratefully accept.
When it comes down to it, elegance is fine to a point. On a typical day of heavy coding, I end up writing upwards of 1000 lines of code. I want my tools to get in my way as little as possible. If that comes at the cost of elegance, so be it. I’d rather follow C# into town for a beer.