I don't really blog anymore. Click here to go to my main website.

muhuk's blog

Nature, to Be Commanded, Must Be Obeyed

March 07, 2015

Capitalize: Lessons Learned

Capitalize was a small (tiny really) project to practice ClojureScript. I think I have learned enough to justify a blog post. Since ClojureScript is basically Clojure with a few differences I am focusing on tools and libraries.

This is not going to be a whiny rant. ClojureScript a great language to write web-based applications. It is a Lisp, it has great support for immutable data and it enables some sort of parallelism[1]. But getting started and then getting productive with ClojureScript is not as easy as Clojure. If you are going to learn ClojureScript, expect a rough start. It will be worth the pain.

I am assuming you are using Leiningen. If you are not, please do.

Configuring ClojureScript Is Not a Walk in the Part

When you are working with Clojure, Leiningen takes care of compilation for you. When you are working with ClojureScript you typically want to configure three builds; an unoptimized dev build, a test build and a fully optimized production build. You also need to configure environments these builds are embedded into.

All of this is well documented. I had help from #clojurecript on FreeNode but all issues I had could have been googled somehow.

You will get error messages like this:

Compiling "target/cljs/test/capitalize_test.js" from ["src" "test"]...
Error in file: "/.../capitalize/target/cljs/test/capitalize_test.js"

ERROR: cemerick.cljs.test was not required.

You can resolve this issue by ensuring [cemerick.cljs.test] appears
in the :require clause of your test suite namespaces.
Also make sure that your build has actually included any test files.

Also remember that Node.js can be only used with simple/advanced
optimizations, not with none/whitespace.

[ReferenceError: goog is not defined]

I had indeed :required [cemerick.cljs.test :as t], so I was stroking my beard thinking what did I do wrong? If I read past the third paragraph I would know it’s because I was using nodejs with :none as optimization level.

Good news is, once you configure your project, you won’t need to change it often.

ClojureScript Doesn’t Lend Itself to TDD Too Well

Doing TDD requires a lot of switching between code and tests, running the tests in between. ClojureScript compiler does incremental compilation and it is reasonably fast. But when you compile all three builds, because you can’t start cljsbuild with specific builds[2], each time you change something you need to wait a while.

Running your tests means executing a shell command. You need to have some program that can run JavaScript in your system and you need to have a wrapper script for the test framework you are using. AFAIK these can’t be pulled by Leiningen as project dependencies.

Good news is front-end applications tend to be UI heavy and UI doesn’t need to be unit tested.

I Was Not Disappointed

I have been looking into languages that compile to JavaScript for a long time. Most of them are just syntactic sugar on top of JavaScript. A lot of them require npm, which I’m not interested in. Some of them are really cool, but then you wonder if it will ever have a healthy ecosystem. I was sold on ClojureScript day one. Now I see that I wasn’t wrong.

If you are trying to learn ClojureScript and you are frustrated because something doesn’t work the way you think it should, just remember these:

  • It is probably documented. Check the wiki, then the mailing list.
  • Read all of the error message, especially the 4th paragraph. (see above)
  • Try IRC: #clojurecript on FreeNode. Paste your project.clj and be specific.

[1]Using continuation passing style, via core.async. JavaScript in the browser is still single threaded but you can still run multiple pieces of code simultaneously. There are other tools that can do this, but as far as I know, they all require separate compilation. ClojureScript, thanks to macros and code-as-data, is extensible from within. This is a huge win because you can combine such extensions with ease.
[2]I guess this can be fixed using Leiningen profiles. I’ll try that in future projects.

If you have any questions, suggestions or corrections feel free to drop me a line.