Sunday, January 8, 2012

Familiarity v readability

The topic of discussion at the office today (yes, it's Sunday) is Languages, Verbosity, and Java by Dhanji R. Prasanna, which purports to extoll the clarity of Java compared to more expressive languages like Ruby, Python, and Scala. What it really says is: "Languages that are familiar are more readable."

Of course languages that are familiar to us are easier to read - in consequence, we have a strong bias toward the languages we know. How strong is this bias? Let's look at some examples from Prasanna's article.

The intro specifies that he loves Java with his whole heart. This pretty much determines the outcome for what language he is going to find the most readable.

As an example of Scala's syntax, he writes sum as a left fold across a list, passing the _+_ operator as a function. Um, hello? If I'm going to sum a list of numbers in Scala, I'm going to use the "sum" method on List. This method is much more readable than Java's syntax of a for loop. This example is completely unfair. No language can prevent the programmer from doing a simple operation in a cryptic fashion.

Another, more reasonable Scala v Java example: he finds
string.exists(_.isDigit)
to be less readable than seven lines of Java. This is determined entirely by familiarity. If you're used to asking a list whether it contains an item that has a particular condition, then it won't disturb you to find this answer in one line with the "exists" method.

This also gets to a point where Prasanna and I disagree: what is the goal when reading the code -- is it to know exactly what is being executed, or to know what the program is doing?

Prasanna points out, correctly, that it's easier to tell exactly what is happening in Java code, whether a particular member is a field or a method, for instance. I could nitpick his examples, but... okay, let's nitpick an example. His assertion is that a Java expression is readable without access to context. He claims that
happy happy(happy happy) {
happy.happy.happy(happy);
}

is completely clear. I dispute that. (To be extra nitpicky, it isn't valid Java; he forgot the return.) It could be a method call on a happy field of a happy field of a happy variable. Or, it could be a static method call on a happy class in a happy.happy package. Or a static method call on a happy inner class of a happy class in a happy package. Some of what we call "readability of Java" is dependent on the conventions and coding standards that we use without even thinking about them. We don't expose public fields, package names start with com or org, etc.

Having disputed whether Java makes clear exactly what's happening in a particular expression or statement (even though overall I agree with him that it does make it more clear than Scala or Groovy), I will now dispute whether we care.

The point of Scala and many dynamic languages is that we shouldn't care whether a particular member is a field or a method. If it yields the value we want, why should we care? Exactly like the Java for loop that is replaced by a one-line method call on a list, Scala lets us hide the implementation details. We don't need to worry about how we determine whether a list contains a particular item, and we don't need to worry about whether we're accessing a method or a field. Work at a higher level of abstraction. That's really where the efficiency increase comes from. It has nothing to do with typing fewer characters. It has everything to do with how much stuff we have to hold in our heads. If we can stop worrying about how we flip through the list, about what's going on under the covers in that expression, then we can hold more of the higher-level code in RAM. Then we can be better readers and writers of code.

I agree that Scala has more ambiguity than Java. The result is a higher level of abstraction, and therefore efficiency.

Admittedly, when you're ferreting out a bug, it helps to know exactly where a line of code will go. We have debuggers for that.

Prasanna also extols Java for being similar to C++, "which is buried deep within the collective consciousness of most programmers." No -- it is familiar to him and to his friends. There's a whole generation of programmers who've never used C or C++. When you're defending a language based on its resemblance to its historic ancestors, you're making my point for me. Familiarity is not a property of the language; it is a property of the current state of your brain.

The CoffeeScript example he gives - of a space making the difference between between creating a new object or not - if it is accurate, then I agree that it is a language flaw. All languages have them, and programmers develop conventions to circumvent them.

Finally, in the concluding section, my point is made when he says that Scheme is incredibly expressive and readable. Really? Scheme is readable? It is one of his favorite languages -- therefore it is familiar to him, and therefore it is readable.

If you've ever gone from using Windows to using Mac, everything is harder for a few days. If you choose to adjust your user model, then you'll find the Mac perfectly usable. Programming languages are the same way, although it takes more than a few days to make friends with a language, especially one as deep as Scala. We as programmers have the capability of learning new idioms and new ways of looking at concepts and constructs. Let's not blame the language for our lack of familiarity. Let's learn instead.

1 comment:

  1. Even using scheme for years, its still not readable. I think he was drinking :)

    ReplyDelete