Pitfalls to Avoid When Moving from Clojure to ClojureScript

I’ve been excited about ClojureScript, its community, and the new tools and libraries that have been appearing, but I’ve only recently started working with it. Using ClojureScript to power a web (or mobile) client to a Clojure backend service seems really compelling. It’d allow for easily sharing code or logic between the different components, as well as a well-designed implementation of modern UI development, via Om Next. It’s also a fun and enjoyable language.

However, while exploring ClojureScript, I’ve run into some areas that left me feeling frustrated.

The Usual Stuff

If you’ve done any amount of reading about ClojureScript, you’re probably aware that there are only atoms for mutable state, no STM. You’re probably also aware that macros are a bit different. In fact, quite different. Given that the official wiki page on the Differences from Clojure is quite thorough, I’m not going to reiterate it. You should definitely read that page first.

Where did doc and source Go?!

When reading tutorials, I’ll often see instructions to jump into the namespace containing my app-state atom. Then after arriving, the very useful source and doc macros are gone. This isn’t too much of a surprise, as it would also happen in Clojure. The difference is that it’s not obvious what namespace you need to get them back:

(require '[cljs.repl :refer-macros [source doc]])

Also note the use of :refer-macros instead of :refer.

Disappearing Macros?

To verify that the macros actually did get interned into my namespace, my first inclination was to simply reference them. I expected to see an error message reminding me that I can’t take the value of a macro. Instead, I was greeted with the same error as before:

my-ns=> source
WARNING: Use of undeclared Var my-ns/source at line 1<cljs repl>
nil

Despite this, I could use it with no problems. This is because, in ClojureScript, a macro and a function can have the same name. For a good read, see Mike Fikes’s blog post.

Why Can’t I Import This Namespace?

A couple of times, while attempting to reference some documentation for a library, I’ve run into confusing error messages telling me that a namespace doesn’t exist.

(require '[natal-shell.core :as nsh])
clojure.lang.ExceptionInfo: No such namespace, natal-shell.core...

This originally made me think that I had incorrectly specified my dependency in my project.clj. Everything was correct, however, and I was left thoroughly confused. Looking at the source, I realized that there’s only a single macro defined in this namespace. Indeed, I had to use refer-macros.

(refer-macros '[natal-shell.core :refer [with-error-view]])

And it worked as expected.

Integrating with Vim

After gaining an intuition of the ClojureScript REPL and its handling of macros, my next barrier was integration with Vim. I’m used to using vim-fireplace for getting inline docs, source, and evaluation.

Thanks to Chas Emerick’s piggieback, that’s fairly straightforward. After following its instructions, I found that fireplace wasn’t working, displaying “Unable to resolve symbol: symbol”.

The fix is to make sure that you have cider-nrepl installed.

Unfortunately, I’m not sure if this allows Vim to handle both Clojure and ClojureScript code at the same time.

Conclusion

ClojureScript has a very different “feel” to it than Clojure, at least at the REPL. I found it to be rather unpleasant, coming from Clojure. As you’d expect, there are good reasons for all the differences. It’d be nice if the error messages provided more help for seasoned Clojurists, but, hopefully, this post will help out those with similar experiences.