Skip to content

Fun with Clojure

April 10, 2010

I really like playing around with new languages but usually I don’t stick with them too long. My day job is coding Java/Javascript and there are few opportunities at work to play around with other languages. As a result most of my experimentation ends up being after hours. Once Ive learned a new language without a practical use to put it to I generally tire of using it.

More recently I’ve started learning Clojure. Clojure has really kept my interest more than anything I’ve learned in recent years. I won’t bother giving an introduction to this language. There are already some excellent resources for that. What I will do it just rattle off some of the things I really like and dislike about the language.

Firstly we have the language itself. It’s a lisp but it’s not the same as Scheme or Common Lisp. Clojure is built on top of the JVM and this makes it a particularly practical language despite it’s young age. The full Java ecosystem is available to use. It very much brings it’s own flavour to the table while keeping many of the things that make lisps great – macros, simple syntax etc. It takes the functional aspects from lisp and turns it up by making immutability the default thing.

I’ve heard Clojure described as having controlled mutability. That seems to be a fairly accurate description. The mechanisms it provides for mutability go beyond standard lock and wait. One of the more novel concurrency features is support for a thing called software transactional memory. This basically brings database like concurrency to every day variable access. While not new STM isn’t widely used so it will be interesting to see how it pans out in a language designed from the ground up to use it. There are plenty of other concurrency mechanisms and you can even go back to plain old locks if it suits your particular problem but what I like about STM and the other concurrency options is that they make reasoning about concurrency much easier. Be sure to read Rich Hickey’s essay on values/state and identity for a great rational on why Clojure handles mutability the way it does.

The biggest problem I had adapting to Clojure has been dealing with a world where I can’t mutate values. It changes the entire way you go about solving problems. Many times I’ve felt I’ve been relearning how to program. Usually coding this way takes me a little longer, no doubt because I’m just not used to coding in a purely functional manner. The resulting code generally turned out simpler in the end however. The reason for this I believe is that without mutability time no longer becomes part of your solution so your code becomes much closer to a pure mathematical expression. This, combined with Clojures terseness and composability, make for some expressive programs.

But enough gushing – there are definitely warts too. While being a language designed for the JVM has definite positive points – it certainly has its down sides too. Clojures types tend to mirror Java’s types. For the most part this isn’t an issue but in order to represent things like numbers Clojure uses the Object representation of numbers. For instances doubles get represented using the Double object. While you can construct real doubles these will always be automatically be cast to Double objects when passed to other functions. This means a lot of stuffing around when trying to get good numeric performance. Later versions of Java have escape analysis but this only gets you so far (and isn’t on by default AFAIK).

My other complaint with clojure is the stack traces. Even for trivial programs they’re long, verbose, nested multiple times and aren’t demangled by default. They also tend not to include much in the way of context.

Lets take a look at an extremely simple example. Real world examples tend to get a lot more verbose.

Say we have the code “(map)”. Since map expects at least two parameters we expect this to fail. Here are the results.

Exception in thread "main" java.lang.IllegalArgumentException: Wrong number of args passed to: core$map (test.clj:0)
        at clojure.lang.Compiler.eval(
        at clojure.lang.Compiler.load(
        at clojure.lang.Compiler.loadFile(
        at clojure.main$load_script__7405.invoke(main.clj:213)
        at clojure.main$script_opt__7442.invoke(main.clj:265)
        at clojure.main$main__7466.doInvoke(main.clj:346)
        at clojure.lang.RestFn.invoke(
        at clojure.lang.Var.invoke(
        at clojure.lang.AFn.applyToHelper(
        at clojure.lang.Var.applyTo(
        at clojure.main.main(
Caused by: java.lang.IllegalArgumentException: Wrong number of args passed to: core$map
        at clojure.lang.AFn.throwArity(
        at clojure.lang.RestFn.invoke(
        at user$eval__1.invoke(test.clj:1)
        at clojure.lang.Compiler.eval(
        ... 10 more

The stack trace isn’t too bad length wise, in real examples they tend to be much larger. Given my test code is only 1 line long I don’t really want to know about the Clojure compiler internals. Here are some other problems as I see them.

  • It includes a nested exception which uglies up the stack trace. We really only care about the root cause.
  • The line number referenced at the top (test.clj:0) is different from the line number in the root stack trace (test.clj:1).
  • The error doesn’t include the actual number of arguments passed or the expected number of arguments. For extra points it could have included the documentation string of the method we were trying to call since that information is included in the metadata.
  • The stack trace contains a lot of internal clojure calls which obscure where the real error is. In an 18 line trace like this one it’s not hard to search through but frequently real world stack traces can span over 100 lines.
  • Clojure function names are mangled. They need to be translated to work out where the real error is.

JRuby stacktraces aren’t reported like this so I see no reason why Clojure stack traces need to looks like Java ones. So in general while the output produced is adequate it’s not really optimal IMHO.

On the other hand one really helpful thing is having a REPL available. Lisps work really well with a REPL. The immediate feedback you get from a REPL is a huge help during development and testing. It’s easy to create little experiments during coding then paste them back into your main code if they work out.

In fact with EMACS and slime I can easily modify my code directly and send it straight to the REPL with a single key sequence. Instantly changing method definitions in running programs. Powerful, useful and wonderful. In fairness to Java you can kind of get that with hotswap but it is slower and more limited.

There’s so many other things to like about Clojure. Destructuring is a great way to pick out the bits you need from a data structure. The persistent data structures mean sharing stucture without worrying about aliasing problems.

Another feature I love is the way Clojure makes a lazy sequence out of everything. Sequences are a powerful abstraction that are a core part of clojure. Finally a language that makes composition practical. In many languages instead of libraries you get frameworks however with Clojure I’ve noticed very few frameworks. I believe the reason for this is because composition in Clojure is much easier than with most languages. Most frameworks seem to be to built get around the limitations caused by the host language. They suffer from the problem of forcing you to code your application around the framework rather than simply making use of functionality in an API. The combination of features such as macros, sequences, first class functions and multimethods provide a the ability to define much more flexible APIs than most languages.

Since I mentioned multimethods, let me go into that for a bit. For many years I’ve been a big believer in object oriented programming. Part of the draw of object oriented programming is the ability to polymorphically dispatch calls based on the type object. I’ve always considered this to be a core feature of OOP. Clojure is a functional language but it turns out you don’t really need to have objects to support polymorphism. This may be obvious to those exposed to multimethods in other languages but it was something I’d never considered before. It turns out that Clojures support for polymorphism is more flexible than a typical OO language because you control the definition of the dispatch function yourself. This means that it is possible to apply polymorphism in ways that would be completely alien in an OO language.

Anyway I’ve been rambling long enough probably. I could easily list many more things I like, but for the moment I’m done. Thanks for reading.

From → Clojure

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: