Miserable Old Duffer's Guide to Working With Clojure
08 April 2020

I’m not going to talk about the language, or its philosophy, or its pitfalls or best practices, or any of that. I’m going to talk about the most significant, but least expected, barrier to learning Clojure: workflow.

It took me a while to work out how to work with Clojure.

REPL

Everyone bangs on about the REPL. I guess if you come from a compiled language background, it’s pretty exciting, but my gateway drug to Clojure was Ruby, and I’ve clocked up a lot of hours in irb.

I immediately felt comfortable in the standard Clojure REPL just like IRB, and it’s fine for that, but I found Rebel Readline, with its ability to highlight, automatically pop up function arguments, and balance parentheses, made the experience much better.

REPL Integration and Editing

The REPL was great for getting started, and it’s quite feasible to put together complete programs in there, but when I learn a language I’m more comfortable writing something, running it, and iterating like that. This is where I hit a snag, because the JVM start up time makes it impractical to work like that.

A good Clojure work flow requires integration of REPL and editor. That is, an IDE. I’ve tried various IDEs: I want to like them, I want to get it, but I never get on with them. I want to use vi, and proper vi, not sketchy bindings in something built not to use them. I use a Xerox Alto style portrait mode screen with a pretty low resolution, and pretty big text (I’m old, okay?) and modern IDEs are absolutely incompatible with that layout.

As I was picking up the language syntax, and writing 20-line programs, I quite liked Nightcode, but I couldn’t use it for anything non-trivial. I tried Atom, Lighttable, even Emacs, but whatever the long-term benefits, twenty-five years of hjkl habits are not easily overcome. And learning Clojure was enough on its own, without having to learn an IDE at the same time.

Eventually I bit the bullet and installed vim-fireplace. This gives me a close enough integration with the REPL, and all I had to learn was a few new bindings.

I also have vim-paredit, which takes care of bracket and quote balancing, and rainbow-parentheses makes the reading a little easier, particularly if you have a light-on-dark colourscheme. (Which I generally don’t.) It’s true what Lisp people say though: after a little while you don’t notice the parentheses.

To work on a Clojure project, I cd to the directory and run

$ lein repl

then once it’s up, open a new terminal tab and vi the file. I can evaluate the expression under the cursor with cpp, or run the whole program with :%Eval. Output appears at the bottom of the screen. I’ve seen people do fancier things with IDE REPL integration, but I’m happy with what I have.

Testing

I had the same problem with tests as with execution. When running lein test takes at least half a minute, the appeal of unit tests lessens dramatically.

Now I use lein-test-refresh, a Leiningen plugin that re-runs tests whenever anything under test/ changes. When I open the REPL tab I mentioned earlier, I also open a test tab, and run

$ lein test-refresh

Then I can ctrl-arrow between my full-screen editor window and the full-screen test output whenever I make a change. It’s a super-fast cycle. If I had more screen real-estate I would probably tile two windows together, but then I’d have to move my eyes, and I can’t be bothered with that.

I have looked at a couple of the other testing frameworks, but so far I like clojure.test just fine, and I feel that if I have easy access to mocks and stubs and spies, I’ll use them, rather than putting in the effort to write more purely functional code which doesn’t need such things.

Linting and Static Analysis

I’m a big fan of static analysis, because it can teach you a lot about idiomatic use of the language. I very much like kibit, which integrates with vim, and offers some excellent advice. The same author also provides the linter Eastwood and there’s a vim plugin for that too.

Deployment

Despite – or possibly because of – having worked in operations for twenty years, deploying every kind of artefact on every kind of platform, I’m a big fan of Heroku. It supports Clojure with virtually no effort, and works just great. Merge to a release branch, and boom, it’s live. Lovely.

I’ve also deployed Clojure code into Amazon ECS using Docker. For minimum resistance, the images on Docker Hub work just fine (I favour the Alpine flavour), but it’s easy to start from a bare OS image and chuck in a JRE and Leiningen.

Done

That’s my workflow. It’s simple and a bit old fashioned, which is probably why it suits me.

tags