Custom Search

Tuesday, November 23, 2010

Evaluating Clojure

The point of the X10 Controller web site project was to put together a simple but not trivial project in Clojure to evaluate the language. I think it served that purpose well.

Clojure is a modern LISP. It reuses the best parts of LISP - simple syntax, functional programming, and a powerful macro facility - and adds modern data structures, high level concurrency tools and concepts like abstract data types. And then it all runs on top of the JVM, making it relatively easy to generate the various Java blobs that deploy as applications, to web servers, or out on the cloud. The parts mostly fit together well, but it's still a work in progress. For instance, the protocol, records and types facilities are new in 1.2, and flagged as "experimental".

Overall, I like the language. It still retains enough of LISPs flavor that my time spent with similar languages makes me comfortable there. Better still, the modern data structures are integrated into the language better than I remember them being in older LISPs, with tools for reaching through multiple levels of structure much like one would use the c[ad]*r functions on lists. The end result is that I can create short, conscise, but readable code for navigating relatively complex structures. The simple
(get-in *devices* [display what]) which finds a device or returns nil if it doesn't exist would take several lines in most language, as you'd either have to check the first index to see if it exists, or deal with the exception thrown if it doesn't.

While I wasn't really dealing with concurrency issues here, the concurrency features - which are part of what drew me to the language in the first place - included a facility that exactly met my needs, even though they aren't what it was intended for. That kind of serendipity is a good sign, and I'm looking forward to exploring those features in depth later.

The Java integration, on the other hand, is a two headed beast. Some consider it an abandonment of what makes LISP LISP. On the other hand, it's also part of what attracted me to Clojure. Classic LISP systems did not integrate well into the environment - they wanted to be the environment. There have been a number of attempts to deal with this issue since I last worked with LISP, but none that really captured my attention.

Clojure solved that problem, mostly very neatly. I had little trouble using the available Java libraries for dealing with either serial ports - which would have required wrapping non-Java code at some point in any case - or the MC17A library. Creating a deployable executable was a bit more work, but more on that later. Finally, another bit of serendipity: the protocol and record structures - designed to make it easy to generate objects that interface with Java code - also turned out to be exactly right for both X10 controllers and X10 devices - at least as I needed them. They felt much lighter than the Python versions of these things I had been contemplating, though in all fairness they are also less capable. On the other hand, that they are designed for Java interop instead of being LISP functions means I had to wrap them in functions to be able to use them the way I wanted.

Now for the bad side. In spite of running on Unix systems, the Java environment does not share the philosophy of Unix systems. In particular, I'm using to simple things being relatively simple. I.e. - I can build an executable and deploy it without needing some kind of file describing all the bits and a tool that uses that to pull them together, though that facility is there if I really want it. In the Java world - and hence in the clojure world as well - I don't seem to have a choice. The Java environment, in particular, seems to follow the language in being very verbose for these things, taking a lot of what's essentially boilerplate and repeated information to get things done. Where the tools have been reimplemented in Clojure, things aren't so bad. But in this area, as elsewhere, things are still a work in progress.

The environment makes Clojure a poor choice for projects much smaller than the X10 controller - the capabilities scattered across four files and another four support files would probably have wound up in three files with no support files in Python. On the other hand, the expressiveness and power of the language makes it quite pleasant to use in projects large enough that the infrastructure is a convenience instead of a pain, or where deploying a Java blob of some kind is part of the goal.

I look forward to getting to know Clojure better in the future.