Learning Clojure: Error messages

I started my recent Clojure talk by enumerating the “scary bits” for new users. The two I went into were the fundamentally different semantics, and that the doc strings for the core functions were succinct and exact, but not expansive in use cases.

There was one that I forgot to bring up, though: compiler error messages when there’s a problem with a macro call.

There’s been a lot of chatter on the topic this past week, since Colin Fleming gave a talk at Clojure/Conj on the subject focusing on how to improve them with grammars. The conversation has kept a positive focus on how to fix it, as opposed to piling negativity on the situation (go Clojure community!) but that does not mean we can expect to see a change right away.

I’d like to address how this might affect you if you’re just getting into the language, and what you can do about it.

To avoid repeating Colin’s points, you may want to check the examples he gives at 6’34” into his talk.

Colin Fleming clojure errors

One thing struck me when I saw these examples: how weird it was that I completely forgot about this issue when preparing my talk.

I suspect it’s one of those things that you learn to live with, having learned to sometimes scry the root cause from the spilled entrails of an error message, so it slipped my mind. Perhaps that’s the reason why it hasn’t been addressed yet - as you get more experienced with the language, you learn to interpret these edge cases. Experienced people forget it’s a pain point for newcomers, and you need to be very experienced if you’re going to be able to contribute a fix.

It’s a similar case to the one I brought up when talking about the into doc string which, in case you don’t know it by heart, reads:

Returns a new coll consisting of to-coll with all of the items of from-coll conjoined. A transducer may be supplied.

How I phrased it at the time was that the doc string tells you exactly what the function does, but nothing beyond that. It does not give any other information that might help someone who’s just getting into Clojure, curious about what this function does, or who might not be familiar with the fact that conjoin - available in Clojure as conj - has a specific, collection-dependent behavior.

The situation with macro error messages, as Colin points out, is not that they’re spewing out an incomprehensible error. They’re telling you exactly what went wrong in the macro. They are not, however, telling you something that would help you find where the problem is in your code.

The question is… will this be a show stopper for you as a newcomer?

I’d say I don’t expect it to be, but would need to add a huge asterisk right besides it.

The asterisk comes from the fact that Clojure’s small syntax can make it easier to eyeball where a problem is, but you need to take the time to get used to it. You need to interiorize that, to use defn as an example, the brackets following the function name are not some peculiar syntax construct, but an actual vector being passed as the second parameter to a macro.

I’ll admit this is easier said than done, and an offshoot of Clojure being simpler than you expect. Even after two years with it, my brain keeps expecting half of what I see to be case-specific syntax. It’s hard to deprogram yourself.

This will, however, bite you particularly hard if you expect to learn Clojure by jumping in and hacking on a project template, something I already warned against. If you attempt to cargo cult the learning process by modifying bits of code in a large program, chances are any errors you trigger will stump you all the way back to Java.

If instead your first programs are small functions, you’ll get much better acquainted with the language, and will have an easier time spotting where you’ve made a mistake when you stumble upon one of these.

It bears repeating. You need to start small.

Get a book so you can learn the basics. Don’t try to jump to something topic-specific (Clojure for web apps!, Clojure for financial analysis!, Clojure for Chrome extensions!) right away. And more importantly, practice.

The advantage of doing the 4Clojure exercises is two-fold. First, it’ll get you used to the Clojure way of thinking. Second, it will provide an opportunity for you to try your newly acquired skills in a localized setting. If you ever trip one of these obscure-sounding errors, you’ll get to see it in the very specific context of the problem, making it easier to realize what your mistake was.

By the time you feel ready to move on to working on larger problems, you may forget that was ever an issue.

I don’t mean to sound like an apologist. It’s something that definitely needs to be addressed if Clojure is going to have a smoother learning curve, which is fundamental for adoption. Things will improve, however, and I expect they will improve rapidly. All the work Colin has done for Cursive - a superb Clojure plugin for IDEA - is not Cursive-specific, and the community as a whole can benefit from it.

Meanwhile, keep it small, and practice!


Ricardo J. Méndez