github twitter linkedin instagram email
Clojure & Ring
2011-09-20

Paul Graham’s enthusiasm for Lisp was a major influence on me as I was learning to program. But for whatever reason I never actually wrote much Lisp code. Recently, I tried writing a small web app in Clojure, a dialect that’s been gaining (a little) popularity recently, and I’m pretty excited about it.

What I like about Clojure:

  • You can exploit the wide range of existing Java libraries, since it runs on the JVM.
  • Its design is relatively clean (unlike my perception of Common Lisp).
  • It’s designed to encourage the use of purely functional code where possible. Such code is at least, in my opinion, more elegant (and way more fun), but I believe it will be more maintainable and testable as well - though I haven’t worked on a large enough project in a functional language to see how this works out in practice.
  • The web stack seems to be going in the direction of a number of small-ish components you can mix & and match, instead of a monolithic framework.

You can get started with Clojure using one of the main build tools, Leiningen or Cake.

Ring

Ring provides a beautifully simple functional model for writing web applications. Your web app is a function that takes one parameter - a map containing the request information - and returns a map containing the response information.

Illustration of a handler function, not really informative

To abstract out common request-processing functionality there’s a convention of creating ‘middleware’. These are functions which take your handler function as a parameter, and return a new function to use in its stead; that new function calls yours, but puts extra information in the request map (and/or does post-processing on the response map). This is how basic functionality like parsing the query params and supporting sessions is implemented. Ring defines a function wrap-params:

Illustration of how wrap-params transforms a handler function into a new handler function

lein-ring is helpful for developing with Ring; it provides a development server with automatic reloading when source is changed (like Rails and Grails, etc), and can handle producing a WAR file for deployment.

Routing

Obviously most web apps will have several endpoints (“/”, “/purchase”, “/feedback/whine”, …). On the off-chance my writing in the above section was lucid, you might be thinking you’d need ridiculous code to implement this in Ring - a function with a giant case-statement or something, with a branch for each path.

Compojure or Moustache lets you write routes much like in other web frameworks. But they just use your routes to produce a normal Ring handler function for you. I like this; it’s related to the “components you can mix & match” idea.

One apparently missing piece (at least with Compojure, the only one I’ve tried) is a way to generate paths based on your routes. In Rails, if I have a route connecting “/foo/:id” to some action, there will automatically be a method that takes an ID as a parameter and returns “/foo/ID”, to be interpolated into hyperlinks, etc. This is quite convenient (and makes it less of a pain to change URLs); using Compojure, I’ve had to create such a function manually for each sort of path I’d need to generate.

Rendering

Two libraries for producing HTML are Hiccup and Enlive. Hiccup tries to make it convenient to generate HTML directly in the code, for instance:

(defhtml my-snippet [ounces]
  [:div.amount-report
    [:span#phlogiston-amount ounces] " ounces of phlogiston available"])

With Enlive (which, as its wiki points out, is also useful for other things like screenscraping), you put your HTML in its own file, perhaps e.g.

<div class="amount-report">
  <span id="phlogiston-amount"></span> ounces of phlogiston available
</div>

Then write code that will inject the dynamic portions into it, something like:

(deftemplate my-snippet "my-snippet.html" [ounces]
  [:span#phlogiston-amount] (content ounces))

I went with Hiccup for my project. I think it makes sense to keep the full power of the language right at hand at all times, so that I can easily define functions or macros to automate repitition and boilerplate in the markup (this is the idea behind, say, stylesheet_tag in Rails; Hiccup similarly has an include-css function). Admittedly, though, I only looked at Enlive very briefly.


Tags: code

Back to posts