Building Noughts-and-Crosses in ClojureScript

I've been experimenting with ClojureScript for front-end development to avoid the hype and chaos attendant to JavaScript. I've been particularly taken by Om, from David Nolen (whose blog, webinars, and conference talks are incidentally fabulous and highly recommended). Om has practical upsides like good documentation and an active community. It also enunciates a coherent picture of how to do front end development with immutable data structures, a virtual DOM, and the power of Clojure's core.async.

The Project

NAC is a simple noughts-and-crosses (or tic-tac-toe) game implemented in about 200 lines of nicely formatted and commented ClojureScript (and another ~100 lines of HTML and CSS). Adopting some of the tricks in David Nolen's core.async webinar, I settled on a design where the leaves of my DOM tree generated events when clicked which were propagated through core.async channels towards the app's top-level until they reached a scope where they could be effectively handled.

For example, clicking on a filled space in the board generates an event which is immediately squashed. A filled cell knows that nothing will come of that action, even with its narrow perspective.

By contrast, a click in an empty cell needs to propagate to the top level (where we track whose turn it is) in order to move the game to its next state.

This sort of architecture certainly has pitfalls, and I expect having to deal with more kinds of event and a more complicated app-state will require more careful design. ClojureScript provides lots of facilities for working with this kind of hierarchy; I used none of them, but it's comforting knowing they're there for when I want to build something less trivial. The broad strokes of the architecture felt suitably de-complected and scalable.

Some Headaches

The ClojureScript build process and ecosystem is (I hear) much-improved from the bad old days, but it's still by no means a straightforward and smooth process. The documentation for the cljsbuild Leiningen plugin is wanting,

Hopefully Chestnut, (a collection of best-practices and solid tools for interactively building and compiling front-ends with ClojureScript) will alleviate some of these problems. It's been very promising to work with, and I hope it goes some way to alleviating the worst headaches of developing interactive front-ends with ClojureScript.

The Virtuous Learning Curve

Om and ClojureScript are less intuitive, smooth, and approachable than front-end frameworks with bona fide beginner-friendly tutorials (such as Ember or AngularJS) and vanilla JavaScript with its vast community and extensive tool set. However, I expect the extra ceremony to be more than work the benefits of real modules and dependency management alone, and the payoffs of using pure functions on immutable data-structures aught to increase with use.

The extra work put in to get from zero-to-sixty with ClojureScript isn't arduous, and it feels like laying good groundwork for further acceleration.

Nat Knight — 2014-12-07