Sat 21 Mar 2009

Mulling over Java

I'm on my way to ILC 2009 — in fact, I'm writing this using the in-flight wifi, which is awesome — and reading my RSS feeds. This, coupled with a week spent fighting with Java, prompts me to note some observations.

Firstly, conditional compilation. This is something that C and Lisp users will particularly notice, should they do something that requires it. In the C case, one often conditionally defines types, macros, etc., to work around platform-specific quirks. In Lisp they're used in the same way, but more often (for me) to add implementation-specific layers, such as using Allegro's SHA-1 library on ACL, but importing and using Ironclad on SBCL.

I've recently been porting some software from one Java application server to another. These servers have minor differences — helpers have different packages or names, certain functionality is only available through implementation-specific classes (which must be cast), and so on. I'd like to conditionalize those implementation-specific portions, so I can import the right classes for each implementation, and have the whole caboodle compile successfully for each server.

Unfortunately, the whole “write once, run anywhere” mantra apparently leads to a belief that conditionalization is unnecessary. With no conditional compilation in Java, I have to resort to two forks of the same code (i.e., using version control and separate builds to handle the distinctions), massively change code to use reflection instead of knowing types at compile-time, or hack around it using Ant (not an option in my case, as only one platform uses Ant for building). I opted to simply fork, because I intend this to be a one-time deal, but I don't like having to make that decision.

Secondly, Java libraries are mostly crap. Many build-your-tower-on-the-JVM advocates suggest that it's a great choice for language implementation because of the available libraries. Unfortunately — and Java joins Python in my book for this — many of these libraries are badly written, inefficient, incomplete, or badly documented… if not all of those things. (This is certainly the case where I've seen the source.) And unlike a dynamic language, to fix one of these libraries means finding (or obtaining through decompiling) the source, recompiling, and inserting back into the JAR; one cannot simply redefine a method within your own code, or dig into an object's private members to work around a bug you can see so plainly in the source.

This is actually not a problem I've observed in the Lisp world. Perhaps it's down to the people, or perhaps the language, but Common Lisp's libraries are uniformly either powerful and complete (e.g., Edi Weitz's code), or documented as trivial. Furthermore, any omission or weakness in the library is mitigated by extension points, adding your own method hooks, or simple redefinition. For example, I recently hooked into the internals of Allegro's SOAP library to check for a non-read-only SOAP request before it actually called into my SOAP handler, allowing me to redirect the request using plain HTTP. That simply wouldn't have been possible in Java: with no :around methods, I'd be reliant on some thoughtful person having added the appropriate pre-request handling interface.

Anyway, enough ranting. Don't even get me started on the verbose syntax, lack of multimethods, and all the rest.

Posted at 2009-03-21 10:47:00 by Richard NewmanLink to Mulling over Java