Save Java, Dead or Alive!
Whatever language you write in, your task as a programmer is to do the best you can with the tools at hand. A good programmer can overcome a poor language or a clumsy operating system, but even a great programming environment will not rescue a bad programmer.
Kernighan and Pike
When I attended Richard Öberg’s talk on Qi4J at the Oslo JUG last week, I started thinking about “Rädda Joppe”, a Swedish children’s show that aired in the eighties. The plot was simple, the young Ola manages to lose his favorite teddy bear, Joppe, and has to learn one or two things about the world he lives in to get him back. Whenever Ola loses his precious bear, saving it is the only thing on his mind and he regularly screams “rädda Joppe, död eller levanda” which translates to “save Joppe, dead or alive”.
The ideas behind Qi4J are quite mind boggling, and I strongly believe that composite oriented programming has a bright future amongst domain driven design practitioners. However, I feel that we’re jumping through too many hoops to achieve paradigm shifts with Java. Richard employs a truckload of black belt dynamic proxy and AOP tricks to achieve a sense of dynamically composable objects in Qi4J, JMock achieves elegance through a programming model that is quite different from your textbook Java and my own Quaere project “misuses” static imports and other language features to achieve its goals. Its fun to push a programming language to its limits, but at the same time we could probably achieve the same more elegantly with other languages running atop the JVM - so I got thinking are we trying to save our favorite programming language the same way Ola tries to save Joppe - without any regard to the world outside?
I’ve always been the kind of guy who explores different programming languages, and during the last couple of years I’ve found my self using different languages from my default choice (which is C# if anyone should wonder) more often. Even if you can write FORTRAN in any language, I find that I get the most out of these programming languages when I take the time to learn the ways of the language. Often I find myself using tricks I’ve learned from Ruby in C#, I was unaware that Visual Basic .NET had static imports until I had learned how to exploit it Java and similar. This is my way to avoid stumbling into “the trap of the Sapir-Whorf thesis”. Applied to programming, the Sapir-Whorf thesis postulates that programmers who are skilled in a particular language or paradigm may not have the same skills with another language or paradigm. One trick ponies still only do one trick.
While pushing a language to its limits paves way for refreshing ideas like composite oriented programming, other attempts to “save Java” fall short in my humble opinion. “Angry” Bill Burke’s rant on dynamic languages was a sad read. Most of the arguments aren’t generally applicable, because there is no such thing as “the dynamic language”. Even for those who believe that Ruby might be a good candidate there are lots of different varieties. Speaking of Ruby, it was Cedric Beust’s little Ruby refactoring challenge, which Bill refers to in his rant, that led me to write this post. Cedric is a clever guy, but I wonder if he’s fallen into the Sapir-Whorf trap with this one. Cedric’s question was what to do with f1(o) and f2(o) while renaming C.init! in this Ruby snippet:
def f1(o)
o.init!
end
def f2(o)
o.init!
end
class C
def init!
puts "Init C"
end
end
class D
def init!
puts "Init D"
end
end
Cedric is right that there is no way for an automated refactoring tool to determine whether these still should be o.init! or o.init2!, but this isn’t a problem caused by Ruby - it is caused by applying the exact same solution to Ruby as one would do with Java - the core of the Sapir-Whorf thesis. Ruby code can be ambiguous, but this is one of the things that makes the language so powerful. Still we need to take this into account when refactoring Ruby code. A simple solution for Cedric’s problem could be to extract a method for the o.init! call, and let this method make the decision on whether init! or init2! should be invoked.
def f1(o)
invoke_init(o)
end
def f2(o)
invoke_init(o)
end
def invoke_init(o)
if o.is_a? C
o.init2!
else
o.init!
end
end
This refactoring could actually be done confidently by an IDE and it is only one of many possible solutions to Cedric’s problem.
Java is a great language, and it runs on an even better JVM. In my opinion, this JVM and the broad selection of top notch frameworks is the Java community’s most valuable assets. But the Java language is falling short of some of it’s peers - just compare the development from C# 1.0 to C# 3.0 to Java 1.4 to Java 1.6. Repairing generics, adding closures and so forth will improve Java, but it isn’t the answer to everything. For instance, it won’t make Java a better option for language oriented programming like Bill claims. C# has got almost everything Bill wants to see added to make Java the ultimate language and C# still is too rigid to be a good foundation to build a DSL atop. Ruby or Python are better choices, not to mention Python’s cousin, Boo, which is statically typed and still behaves like a dynamic language. But do remember; When in Rome, do as the romans. These languages aren’t exactly like Java - the have their own cultures, conventions and style.
When Java saw the light of day, talented developers flocked around because it was refreshing and new. The same thing happened with C#, greatly improving the overall quality of applications developed on Microsoft’s platform. Today you’ll find tons of mediocre code written in both Java and C#. The same thing is happening with Ruby, Python and any other dynamic plate de jour. In the end, talented programmers will have come up with new, refreshing ways to design software and I believe that lessons learned through by getting new impulses from other languages and “programming cultures” is a great way to do so.
Le ${favoriteProgrammingLanguage} est mort. Vive le ${favoriteProgrammingLanguage}!