The recent series of Why XYZ Is Not My Favourite Programming Language articles has been fun to do, and it’s been great to see the discussion in the comments (even if it’s mostly people people saying that I am talking a load of fetid dingo’s kidneys). But I don’t want to extend that series beyond the point of diminishing returns, and it’s time to think about what it all means. As duwanis commented on the Ruby article, “I’m a bit lost as to the point of these posts”; now I want to try to figure out just what, if anything, I was getting at.
By the way, it’s been interesting how people respond to articles that are critical (however flippantly) of languages. Most of what I’ve written here on TRP has had comments pretty evenly balanced between “Yes, I know exactly what you mean” and “You are talking complete nonsense”, which seems about right to me; but comments on the NMFPL posts have almost all been telling me why I am wrong. It’s also been interesting to watch all the Reddit posts for these articles drop to zero, or at best stay at one: evidently people who like languages are keener to defend them than those who dislike them are to pile in — which is as it should be.
Reviewing the languages
First of all, let me say that all the languages I picked on are, or at least have been, good languages. I didn’t bother criticising BASIC or FORTRAN, or Tcl for that matter, because, well, everyone already knows they’re not going to save the world. (I didn’t criticise any of the functional languages because I don’t honestly feel that I yet know any of them well enough to do an honest job of it.)
So, to look at the positive, here are some reasons to like each of the languages I’ve been saying are not my favourites. In roughly chronological order:
- C (1972) was, depending on your perspective, either the first really expressive low-level language, or the first really efficient high-level language. Its historical importance, as the foundation of all but the earliest implementations of Unix, is immense. But, more than that, it has a crystalline elegance that few other languages approach. (I’ll be writing more about C in future articles.)
- C++ (1983), despite being more prone to abuse than any other language, can indeed be used as Stroustrup suggests, as “a better C”. It was also a very impressive technical achievement: to come so close to being object oriented while retaining binary compatibility with C is pretty astonishing. It solves that problem well, while leaving open the question of whether it was the right problem to solve.
- Perl (1987) was and is amazingly useful for just, you know, getting stuff done. It has a likeable humility, in that it was the first major language to make working together nicely with other languages a major goal, and its Swiss Army Chainsaw of text-processing methods were a huge and important pragmatic step forward. It’s not pretty, but it’s very effective.
- Java (1995) can be thought of as “a better C++”; and it is better in lots of important ways. It hugely reduces the amount of saying-it-twice that C++ source and header files require, the code is much cleaner, it is much harder to shoot yourself in the foot, and experience tells us that it scales well to very large projects with many programmers.
- Ruby (1995) is in a sense not really a huge leap forward over previous languages; but it’s done the best job of any of them in terms of learning from what’s gone before. It really does seem to combine the best parts of Perl (string handling, friendliness towards other languages), Smalltalk (coherent and consistent object model), Lisp (functional programming support) and more.
Although there are plenty of other languages out there, these are the main contenders for the not-very-coveted position of My Favourite Programming Language: I am deliberately overlooking all the Lisps and other functional languages for now, as I just don’t know them well enough, and I am ignoring C# as a separate language because even highly trained scientists with sensitive instruments can’t tell it apart from Java; and PHP because it’s just Even Uglier Perl, and Visual BASIC for all the obvious reasons. (I don’t really have a good reason for leaving Python out, but I’m going to anyway.)
Some thoughts on Java
And it’s a good language. At the cost of some expressiveness, it tries to make itself foolproof, and it does a good job of it. In a comment on the recent frivolous Java article, Osvaldo Pinali Doederlein boldly asserted that “there are no major blunders in the Java language”. Surprisingly enough, I do more or less agree with that (though its handling of static methods is pretty hideous). I think that almost-no-major-blunders property is a nice consequence of Java’s lack of ambition: it was pretty much deliberately designed as C++ Without The Compromises To Keep It Binary Compatible With C, and it did a good job of learning from C’s and C++’s mistakes, to produce a language that is much smaller and more pleasant than C++ and perhaps on a par with C for both size and pleasantness while being much more convenient.
My main issue with Java is actually much more pervasive than any specific flaw: you’ll forgive me if I find this hard to tie down, but it’s just a sense that the language is, well, lumpen. Everything feels like it’s harder work that it ought to be: programming in Java feels like typing in rubber gloves.
An obvious example of this is what Hello World looks like in Java:
You have to say a lot of stuff before you can say what you want to say. You have to have a main() function, it has to be declared as taking an array of String and it has to be public static void. It has to be wrapped in public class SomeIrrelevantName (which, by the way, has to be the same as the name of the source file.) The print() function is called System.out.println(). The comparison with complete Hello World programs in other popular languages is instructive:
print “Hello, world!\n” # Perl
print “Hello, world!” # Python
puts “Hello, world!” # Ruby
(print “Hello, world!”) ; Emacs Lisp
10 PRINT “Hello, world” :REM Commodore 64 BASIC
Is it a big deal that Java makes you say public static void main(String args)? No, it’s not. It’s easily learned, and Java programmers develop the ability to become “blind” to all the syntactic noise (at least I assume good ones do). But it’s pervasive. All Java code looks like this, to a greater or lesser extent. How many mental CPU cycles do Java programmers burn filtering out all the keyword soup?
At the risk of looking like a total Steve Yegge fanboy, I’ll illustrate that with an example taken from his article on Allocation Styles: how to ask a language what index-related methods its string class supports (i.e. which methods’ names contain the word “index”). His Java code looks like this:
If you’re a Java jockey by nature, you’re probably looking at that and thinking “well, that doesn’t look too bad” (though quite possibly also thinking about a couple of incremental improvements you would make).
Here’s how that program looks in a less verbose language (Ruby, as it happens):
Now even if you agree with me and Osvaldo that “there are no major blunders in the Java language”, you have to admire the concision of the Ruby version. It’s literally an order of magnitude shorter (30 characters vs. 332, or one line vs. 11).
What a concise language buys you
“But Mike, surely you’re not saying that the Ruby version is better just because it’s shorter?”
Well, maybe I am. Let’s see what the advantages are:
- Less code is quicker to write than more code.
- Less code is easier to maintain than more code. As Gordon Bell has pithily observed, “The cheapest, fastest and most reliable components of a computer system are those that aren’t there.” Each line of code is a line that can go wrong.
- Concise code lets you see more of the program at once: this isn’t as big a deal now we all have 1920×1200 screens rather than the 80×24 character terminals that I did all my early C programming on, but it’s still an important factor, especially as programs grow and start sprouting all kinds of extra glue classes and interfaces and what have you.
- A concise language keeps the total code-base size down. I think this is very important. ScottKit currently weighs in at 1870 lines of Ruby, including blank lines and comments (or 1484 once those are stripped). Would I have started a fun little project like that at all if it was going to be a fun big project of 20,000 lines? Probably not. And this factor becomes more important for more substantial projects — the difference between ten million lines of code and one million is much more significant than the difference between ten thousand and one thousand.
- Most importantly, look at what code isn’t in the Ruby version: it’s all scaffolding. It’s nothing to do with the problem I am trying to solve. In the Java version, I have to spend a lot of time talking about ArrayLists and Italian guys and loops up to methods.length and temporary String buffers and Italian guys. In the Ruby version, it’s a relief not to have to bother to mention such things — they are not part of my solution. (Arguably, they are part of the problem.)
I think the last of these may be the most important factor of all here. I’m reminded of Larry Wall’s observation that “The computer should be doing the hard work. That’s what it’s paid to do, after all”. When I stop and think about this, I feel slightly outraged that in this day and age the computer expects me to waste my time allocating buffers and looping up to maxima and suchlike. That is dumb work. It doesn’t take a programmer to do it right; the computer is smart enough. Let it do that job.
The upshot is that in the Ruby version, all I have to write about is the actual problem I am trying to solve. You can literally break the program down token by token and see how each one advances the solution:
Here we go:
- “” — a string. (The empty string, as it happens, but any other string would do just as well.) (I notice that WordPress inconveniently transforms these into “smart quotes”, so that you can’t copy and paste the code and expect it to Just Work. D’oh! Use normal double quotes.)
- .methods — invoke the methods method on the string, to return a list of the methods that it supports. (You can do this to anything in Ruby, because Everything Is An Object.)
- .sort — sort the list alphabetically.
- .grep — filter the list, retaining only those members that match a specified condition.
- /index/ — the condition is a regular expression that matches all strings containing the substring “index”.
- i — the regular expression matches case-insensitively.
Bonus pleasant property
As a bonus, the code reads naturally left-to-right, rather than inside-to-outside as it would in a language where it all has so be done in function calls, like this:
I think that Ruby’s object-oriented formulation is objectively better than the pure-functional version, because you don’t have to skip back and forth through the expression to see what order things are done in.
“Say what you mean, simply and directly.”
When I started to write this article, I didn’t know what my conclusion was going to be. I just felt that I ought to say something substantial at the conclusion of a sequence of light-and-fluffy pieces. But, as Paul Graham says [long, but well worth reading], part of the purpose of writing an essay is to find out what your conclusion is. More concisely, E. M. Forster asked, “How do I know what I think until I see what I say?”
But now I’ve conveniently landed on an actual conclusion. And here it is. Remember in that Elements of Programming Style review, I drew special attention to the first rule in the first proper chapter — “Say what you mean, simply and directly”? The more that runs through my mind, the more convinced I am that this deceptively simple-sounding aphorism is the heart of good programming. Seven short words; a whole world of wisdom.
And how can I say what I mean simply and directly if I’m spending all my time allocating temporary arrays and typing public static void main? My code can’t be simple if the functions I’m calling have complex interfaces. My code can’t be direct if it has to faff around making places to put intermediate results. If I am going to abide by the Prime Directive, I need a language that does all the fiddly stuff for me.
So it looks like My Favourite Programming Language is Ruby, at least for now. That might change as my early infatuation wears off (I’ve still only been using it for a couple of months), and it might also change as my long-anticipated getting-to-grips-with-Lisp project gathers momentum. But for now, Ruby is the winner. And it’s going to be dethroned, it’s not going to be by a scaffolding-rich language like Java.
Coda: I don’t hate Java
But I am on a quest now — to say what I mean, simply and directly. And Java is not a language that helps me do that.
Update (19 March 2010)
The discussion at Reddit is not extensive, but it’s well worth a look because it contains some nice code samples of how you can do “”.methods.sort.grep /index/i in other languages.
There’s also a little discussion at Hacker News.
And, finally: please ignore this magic code, which I am gluing into the article so that Technorati can pick it up and know that I really am the owner of this blog: EECRHMA873AV