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 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.
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:
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.
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.
(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.
Questions or feedback on this post? Email me at firstname.lastname@example.org