My self-education in enterprise application development continues. I've come to terms with Java, finally seeing its inner-beauty by using it on a task for which it is well-designed. When I was writing inference algorithms for a natural language understanding systems in it for ISI, its abstraction facilities fell far short of my needs. That was a task that pushed the limits of my mind, and Java was a mental straitjacket. Java gives everyone the same abstractions. Everyone plays the same game, sees the same mental images of object graphs and messages. If this picture doesn't work well for your problem, well, that's too bad. This contrasts sharply with a language like Scheme, which gives you the ultimate tools to roll your own abstractions. Need an object system? No problem. You can write it in the language, along with an infinite variety of other constructs you may need. When it comes to flexibility, Lambda is King.
But, at least for the year 2006, this flexibility still comes at substantial cost. Fringe languages entice me with their expressiveness, and for truly mind-bending algorithms, a functional language is the clear choice. But what about more pedestrian tasks: XML schema parsing, HTTP servers, parser generators, database interaction, and the million other tasks that comprise a typical real world application? Such demands quickly take you off the beaten path in a language like Scheme. One of my favorite quotes about programming language is by Philip Greenspun: "Java is the SUV of programming tools."
It's true. And when you want to get off the beaten paths, an SUV is exactly what you need. To write productively in Java I need an IDE which pushes the limits of a dual 2 GHz G5. I need a build tool that can automate the task of downloading and managing hundreds of third party libraries and frameworks that at least in part exist to overcome many of Java's expressive shortcomings. In Java I get 10 miles per gallon of code, but I can drive over anything in my path.
Java offers a lot of valuable lessons for languages of the future, that will hopefully eclipse it (no pun intended) in expressive power while retaining features that make it great. Often my attitudes toward software are hard to pinpoint and explain in words... they're rooted in "feelings" and "intuitions", matters of taste rather than fact. Yet just because they're vague, I don't think such intuitions are without merit. They should be explored and discussed, because software is much more like music than it is like engineering. It's a creative, imaginative mental activity. The key is to translate our intuitions into more useful concepts, trying to pinpoint their origin. Here's some positive things about Java that are more concrete than car analogies.
I used to think of this a bad thing, that more "dynamic" languages were the wave of the future. Now I'm not so certain. The problem with dynamic languages is that the interpreter has the final word on the semantics of programming constructs. This works well for single person development, but I think it has trouble scaling. The behavior of code depends on the state of the runtime environment. In contrast, the behavior of a static language is entirely controlled by the source code itself. It's just a feeling thing, but I like this. There seem to be fewer moving parts to keep track of at once. It feels more solid to have a compiled module than a script that you evaluate in a given environment. Dynamic typing, while more flexible, also makes it much more likely that runtime errors will be produced. Runtime typing may save typing, but it forces programmers to perform type checking and inference in their own heads. It also forces one programmer to perform typing and inference on constructs written by others.
Static languages used to be a problem for usability. You'd write a file full of code, then drop to a shell and run a compiler on it, getting back a list of errors that you'd go back and fix. Eclipse changes this. It takes all the things that used to be painful about Java's static nature and turns them into powerful allies. Everything a compiler checks to ensure valid programs is now checked as you type, so the static design that once provided guarantees at compile-time now provides a kind of "spell check for code." It's invaluable. The static type information also enables excellent code completion and refactoring support. I used to look upon IDEs disparagingly, thinking that a language should be well enough designed that it shouldn't need such a crutch. I now think that a language isn't really complete without such authoring tools. We have computers, we should be leveraging them to help us write better programs in every way possible.
A Great Consistent Ecosystem of Other People's Code
The way Java's packaging system works, its extensive built-in API, and the ecosystem of free-software frameworks that have grown up around it make a huge difference. The classpath is a great abstraction for module reuse, decoupling package use from physical location on the file system. Combined with the tool support discussed above, these features make Java a really nice universe to live in.
It's not a language for writing complex inference algorithms, but I'm learning how to overcome what I thought was wrong with Java. Anonymous inner classes make me feel a little less sad about the lack of lexically-scoped closures. Generics help with Casting Hell. Annotations give you some meta-programming and relieve XML Hell. But it's still certainly not the be-all and end-all for programming. I'd love to see a successor language evolve that raised the theoretical bar while maintaining Java's many strengths.