Some notes on Clojure

The programming language Clojure is enjoying a surge in recent activity and interest, capped by a presentation by its inventor at the end of the Lisp50 program at OOPSLA. Lispy has a good recounting of Hickey’s talk on his blog. There’s interest in forming a study group here in the DC area, under the auspices of Fringe DC. And Clojure is appearing in a lot of blogs I’m reading, surrounded by almost dreamy language like “nirvana” or “I should / wish I were learning this now.” I’ve sat down with the beta version of Stu Halloway’s Programming Clojure, and I’ve read the first third and coded up some examples, and I’m very excited with what I’m finding.

Your Lisp is in my Java

The big story about Clojure so far is that it’s a JVM language: Lisp meets Java. Clojure is Lisp. It has S-expressions, first-order procedures, macros. But it runs on the Java Virtual Machine. Which means it swims in the same sea with all of Java’s native and library code. As Stu Halloway repeats, there is no translation layer between Clojure code and the JVM.

What does this look like in practice? Well, there are special forms in Clojure, like the dot operator, that let you call Java. Prepare your innocent retinas for this code snippet. They may not be the same after you see this melding of Lisp and Java:

(def rnd (new java.util.Random))
(. rnd nextInt 10)

Yep. It does exactly what you think it does. It creates an instance of a Java class and then makes a method call by passing a parameter. Clojure not only plays with Java, it also makes Java kind of… better. The double-dot special form lets you chain method calls and actually reduces the number of parentheses needed to do the same in Java. Here is another example from the book:

(.. '(1 2) getClass getProtectionDomain getCodeSource getLocation)

Clojure duck-types, and it apparently does a good job guessing what native types are needed, e.g. Integer vs. BigInteger. But you can add metadata to your procedures to “give hints” to the compiler about parameters and objects. This makes compiled Clojure the same as compiled Java bytecode and speeds performance where you need that more than convenience or flexibility. Strong typing (only) when you need it.

Speaking of metadata, Clojure makes it easy to add metadata to your objects using with-meta. E.g. (with-meta obj {:key value})). This simple formalization points to lots of possible uses for metadata, in idiomatic Clojure and beyond. I’m keeping an eye out to see where this leads.

Sequences

Everything is Clojure is a sequence. Not a list, per se. (A list is just a kind of sequence.) The sequence abstraction wraps lists, vectors, maps, hashes, sets, and streams (more on that later). So you get the iterative and recursive functional programming constructs you expect. And some things you might not.

In implementing the sequence and macros and procedures that exploit it, Clojure improves things over Lisps I have known with some very sweet syntax. The recur special form is awesome. You can use it with loop or inside a procedure (named or anonymous) to “do it again” without worrying about naming or explicitly defining a recursion point.

Sugar abounds. When you need an anonymous procedure, maybe as a filter or modifier to impose on a sequence, you can shorten the procedure definition (already fairly lightweight) from
(fn [x] (* x 2))
to
#(* % 2).
The # is a reader macro for fn, and the % represents the first (only?) function parameter. As a javascript programmer, I’m very appreciative of this shortcut, since I’ve been writing a lot of stuff like function(x) {return x * 2;} lately.

Clojure extends the sequence abstraction to its interactions with Java, or as Halloway puts it, “Clojure Makes Java Seq-able”. You can use first and rest (and therefore in theory all the iterative and recursive procedures and structures you like) with Java collections, file structures, regular expressions, XML processing, and relational database results. Damn.

Laziness

One more thing so far: lazy sequences and streams. In SICP, there’s a section on lazy evaluation and streams. I was fascinated by the idea of a sequence that is evaluated only when needed. It’s much more efficient in many cases than evaluating a whole sequence (possibly repeatedly). But the SICP book was strangely un-concrete about how to implement such structures in Scheme. Not so in Clojure. Say hello to lazy-cons, lazy-cat, and take.

> (def trapped (lazy-cat [1 2] (throw (Exception. "Booby Trap!"))))
#'user/trapped

> (def whole-numbers (iterate inc 1))
> (take 10 whole-numbers)
(1 2 3 4 5 6 7 8 9 10)

The thing here that blows my mind? Defining whole-numbers with an S-expression that at first glance just ought to return 2 and be done with it. But instead it becomes a lazy sequence. It’s iterate that provides this magic. I’m excited about going back to SICP and implementing the streams examples with Clojure.

Stay tuned

Still to come, concurrency, maybe Clojure’s biggest selling point. Clojure is supposed to make multi-threaded concurrent programs much easier to write by abstracting away locking and synching issues and letting you focus on the real action your code is trying to carry out. With multi-core processors and cloud computing becoming more important every day, I’m starting to wonder why more people aren’t focusing on concurrency. I’m beginning to feel as though there’s a very big change coming, and those who understand how to use the powerful tools now being developed will leave everyone else behind. I guess that’s what people call the future.

Buy the book

Seriously. If you have any interest in the progress of programming languages, or making the power of Java easier to use and extend, or you like Lisp or functional programming (or want to learn about them), you should consider Halloway’s Programming Clojure. It’s in beta, so you can get it in PDF now. There are a few typos and (at the moment) some namespace issues in the example code, but you can help resolve them. And get a head start on a technical tool with a bright future.

Tags: , , , , , ,

Comments are closed.