Ten reasons to use Clojure/ClojureScript on your next web dev project
Hello sir. I’m here to talk to you today about accepting Clojure into your workflow. Do you have a few minutes? I’d like to show you these ten ways that Clojure can help you make better software. Good, here we go!
Executive Summary
- Philosophy: Clojure makes some really smart design decisions
- ClojureScript: It’s not javascript
- Enlive: It will change the way you think about prototyping and templating, at the same time
- Safety: Clojure contains a lot of ways to enforce correct code, if you’re afraid to use a dynamic language
- Concurrency: Clojure is really good at concurrency, and has many great libraries that make it even better
- Interop: Clojure can use Java libraries, and ClojureScript can use Javascript ones, and it’s really easy
- Om + React.js: I started using these recently and find them really exciting
- Macros and DSLs: These aren’t the C macros you’re used to. Build full-fledged languages, that are still Clojure
- Ring: A simple and fantastic API for writing web apps. The best one I know of
- Live Coding: For those who like to type while they think
Disclaimer: I write about Clojure a lot, so forgive me, regulars, if I repeat myself. I just wanted to pull some selling points into one article.
10. Philosophy
Other articles of this sort tend to descend into pedantry regarding Clojure’s language design. I want to focus instead on tools and libraries that Clojure’s great design facilitates, but I can’t go on without mentioning some of the basic ideas that Clojure(script) gets very right:
Immutable data structures
Out of the box, Clojure provides lists (linked lists), vectors (like arrays, but resizeable),
sets, and hash-maps as data structures. All of these are immutable
; functions that update
them return whole new copies; except they don’t, because Clojure implements these data structures
as trees which can share unchanged nodes for memory and time efficiency.
But you don’t have to worry about that. What it means for you is that suddenly, your
software doesn’t have weird bugs like crashes when you call splork
on your
list twice in different spots and you end up with a list that was splorked twice without
your consent.
Tendency towards functional purity (without getting impractical)
This plays very will with immutable structures. Clojure wants you to write functions that accept values, and return values, without changing anything on the outside. The thing is, good code in most languages already does this; it’s a good habit, because pure functions are easy to think about and easy to test.
These two above facts combined mean that your programs tend to be highly modular without you even thinking all that much about it; you write pure functions that call pure functions, and everything keeps to itself. But, not every problem can be expressed simply as a function (by definition, every problem can be expressed as a function, which is why I added that “simply” qualifier). For when you need to share information, you have access to a one-of-a-kind inbuilt software transactional memory system. This makes shared-memory concurrency really really easy. Consider this Java:
; Use a parallel map to count words. Contrived implementation, but it probably works
(def output (atom {}))
(defn count-word [word]
(swap! output update-in [word] (fnil inc 0))) ; Update the atom with a function
(defn word-frequencies [words]
(reset! output {}) ; Reset the atom to an empty map
(doall (pmap count-word words)) ; Call count-word on each word, and do it in parallel
@output) ; Return the current value of output. Blocks until output is done updating.
Let’s walk through (swap! output update-in word (fnil inc 0))
.
inc
is a function that increments a number.(inc 1)
returns 2.(fnil inc 0)
is a shortcut to creating a function that looks like this:
(fn [v default]
(if (nil? v) ; If v is nil...
(inc default) ; return a default
(inc v))) ; otherwise, return (inc v)
update-in
takes a hash-map, a key, and a function, and applys the function to the current value at the key, then, because immutability, returns a new updated hash-map (efficiently, with structural sharing):
(update-in {"hello" 2 "world" 4} ["hello"] inc) ; => {"hello" 3 "world" 4}
swap!
accepts an atom, a function, and an unbounded list of arguments, and updates the value contained in the atom.
(def my-atom (atom 1))
(swap! my-atom inc)
(prn @my-atom) ; => 2
So, (swap! output update-in word (fnil inc 0))
replaces the value of the atom with (update-in <current value> word (fnil inc 0))
.
In short, you can reach right out of your map to diddle your word-count, and you did
it while staying safe. Let’s see those haskell weenies let you get away with that.
Note: There are better ways to count words that don’t involve STM
Ok, that’s the part where we talk about Clojure basics. If you still don’t feel like you have a warm enough introduction, or if the syntax still gives you the willies, I urge you to read through Learn clojure in Y minutes to get the hang of it. It won’t take long.
9. ClojureScript is better than Javascript
It just is! The ClojureScript team has done a truely excellent job creating a language that behaves almost just like Clojure, but runs wherever Javascript does. Immutable data! Atoms! All that stuff above! And all while maintaining full interoperability with the host platform:
(js/$ (fn [] (js/alert "Hello World!")))
; $(function(){ return alert("Hello World!"); });
8. Enlive and Kioo
I’ve written about Enlive before, and I discovered Kioo recently. Both are (used by me as) templating libraries, but they’ve very special libraries.
Most templating libraries take your code, and turn it into a big HTML string. Enlive and Kioo take your HTML string and turns it into code – that is to say, data, I get those confused these days. So then, you can take your structured data representing the HTML, and apply transformations to it. A transformation consists of a css-like selector, and a function. It looks a bit like this:
(kioo/deftemplate "index.html"
[app-state]
{
; Update the count in elements with class "todo-count"
[:.todo-count] (kioo/content (count (:todos app-state)))
; Update the contents of elements with class "todos" to contain the
; todos, rendered with the function `render-todo`
[:.todos] (kioo/content (map render-todo (:todos app-state)))})
deftemplate
returns a function that, given some data, renders a template with that data.
It gets its template from the file you provide a path to. Neat, eh?
The best part about this is that you can just use a regular old HTML file as the input to your template, even one with dummy data in it. This contrasts with other approaches like Mustache/Jinja/ERB/PHP/et. al, which force designers to use some template language.
Kioo is designed to work with Om or Reagent, while Enlive mostly generates HTML strings. A similar library is Laser, which I’ve also used and liked. The choice between Laser and Enlive is a question of mostly taste.
7. Dynamic with benefits: core.typed, Prismatic Schema, Records & Protocols
If you’re concerned about quality software engineering, and don’t want to deal with a language that can’t guarantee correctness on compilation, you really don’t have much to fear with Clojure. In order of increasing assurance:
Clojure mostly shies away from object-looking things, but it makes an exception for Protocols. Protocols can be thought of like Java interfaces, with the handy extra capability of being applied at runtime, which means you can extend whatever class you like with “methods” (which are really just functions with some dispatch machinery):
(import 'java.util.Date)
; A protocol with one method
(defprotocol TimeDelta
(since [this other-date]))
; An implementation of that method on java's Date
(extend-protocol TimeDelta
Date
(since [this other-date]
(Date. (- (.getTime other-date) (.getTime this)))))
Another bit of structure you can add to your Clojure programs are Records, which act like hash-maps with special initializers, but also provide a quasi-type metadata:
(defrecord Point [x y])
(def map-point {:x 1 :y 2})
(def rec-point (->Point 1 2)) ; "defrecord" creates a function -><Name>
(:x map-point) ; 1
(:x rec-point) ; 1
You can use these structures with various tools. Prismatic’s Schema provides some handy methods for creating and validating contracts about data and types in your code:
(require '[schema.core :as s])
(def PointSchema {:x s/Int :y s/Int})
(s/validate PointSchema map-point) ; OK
(s/validate PointSchema rec-point) ; OK
(s/validate PointSchema {:z 1}) ; Throws an exception.
(s/defrecord StrictPoint [x :- s/Int y :- s/Int])
(s/defn add-points :- StrictPoint
[p1 :- StrictPoint p2 :- StrictPoint]
(->StrictPoint (+ (:x p1) (:x p2)) (+ (:y p1) (:y p2))))
Of course, Schema will only run validation when you ask it to. If you want to be really sure, you can use core.typed:
(require '[core.typed :as t])
(t/ann add-points [StrictPoint StrictPoint -> StrictPoint])
This creates an annotation for add-points
, which can be checked by running
(check-ns)
in the namespace. The difference between Schema and core.typed
is that core.typed will recursively check functions inside the definition.
So you see, you can make Clojure as strict as it needs to be, where it needs it, without losing the benefits of writing dynamic code where static typing would be unnecessary.
6. Concurrency Libraries
Another thing I’ve written about, but definitely worth mentioning. Clojure has a few libraries that make concurrency very interesting. There’s lamina, which has an awesomely simple API for creating channels.
(lamina/channel transactional-emails)
(defn finish-signup [request]
(lamina/enqueue transactional-emails
(signup-email-message (-> request :user :email))) ; Does not block
(respond request))
; Consumes emails forever
(future (while true (send-mail @transactional-emails)))
But the real standout these days is core.async
, which is notable for working just
fine in both Java (yawn), and Javascript (exciting!). Don’t feel like writing
callbacks in callbacks? Why not publish all your events to a super-fast asynchronous
channel?
; Some routing function
(defn handle-event [type & args]
(case type
:mousemove (apply handle-mousemove args)
:click (apply handle-click args)))
; Event channel
(defonce event-chan (chan))
; Consume from event channel forever (Asynchronously!)
(go (while true
(apply handle-event (<! event-chan))))
; Pop a click into the channel on clicks
(.click (js/$ "#my-link") #(put! event-chan :click :my-link %))
5. Full interoperability with the host platform
Nobody really loves Java(script)-the-language, but Java(script) still get(s) a ton of use because of the(ir) platform(s). Not wanting to waste that important advantage, Clojure(script) is carefully designed so that you can use Java(script) libraries without having a bad day. Here’s, I’ll show you!
(import 'java.util.Calendar)
(defn today []
(-> (doto (Calendar/getInstance)
(.set Calendar/HOUR 0)
(.set Calendar/MINUTE 0)
(.set Calendar/SECOND 0)
(.set Calendar/MILLISECOND 0)
(.getTime)
(.getTime))))
Which means approximately:
import java.util.Calendar;
public class TodayContainer{
public static long today(){
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime().getTime();
}
}
But that’s not all: host platform operability means wrapper libraries, and Clojure has some pretty amazing ones. Here’s an example of a library I’ve been excited about recently called Flambo, which is a wrapper around Spark (a Scala project):
(ns com.fire.kingdom.flambit
(:require [flambo.conf :as conf])
(:require [flambo.api :as f]))
(def c (-> (conf/spark-conf)
(conf/master "local")
(conf/app-name "flame_princess")))
(def sc (f/spark-context c))
(-> (f/text-file sc "data.txt") ;; returns an unrealized lazy dataset
(f/map (f/fn [s] (count s))) ;; returns RDD array of length of lines
(f/reduce (f/fn [x y] (+ x y)))) ;; returns a value
A full map-reduce word count in Spark in almost no lines. Great stuff. I’ll probably write about that more later. So, in short, building your app in Clojure lets you use some pretty heavy-duty tools, right out of the box.
And what of Javascript?
(-> js/angular
(.module 'todoApp' (array))
(.controller 'TodoController'
(array "$scope" (fn [$scope]
(aset $scope "todos" (array
(js-obj "text" "learn angular" "done" true)
(js-obj "text" "build an angular app" "done" false)))
(aset $scope "addTodo" (fn []
(.push (.-todos $scope) (js-obj "text" (.-todoText $scope) "done" false)
(aset $scope "todoText" false))))
(aset $scope "remaining" (fn []
(reduce + 0 (filter #(not (.-done %)) (.-todos $scope)))))
(aset $scope "archive" (fn []
(aset $scope "todos" (filter #(not (.-done %)) (.-todos $scope)))))))))
Of course, you won’t need angular where we’re going.
4. Om + React
React.js is the hot new javascript “framework” that’s been sweeping the globe – or at least my personal news bubble. I put “framework” in scare quotes because it’s mostly a rendering solution for web apps. However, it does do a really good job of it, storing a “Virtual DOM” in memory to quickly compute diffs in rendering between shared data.
David Nolen’s Om goes farther, by utilizing ClojureScript’s immutable data structures. By representing the Virtual DOM with an immutable object, diffs can be computed efficiently by checking references, not values, and so Om runs that much faster. Also, it uses ClojureScript instead of Javascript, which is itself an advantage as discussed above. But, I won’t waste my time illustrating the benefits of Om when you can hear it from the man himself.
Om is, in effect, the first really popular ClojureScript framework, and with good reason. But if you really want to use something else…
3. DIY DSLs
Short version: Clojure’s syntax makes writing complex macros easy, and macros can be very useful.
Macros are code that writes code. Clojure code is structurally simple, and easy to manipulate with regular collection-processing functions. Therefore, you can do some pretty interesting stuff:
(defmacro defvar [$scope prop val]
(let [prop-str (str prop)]
`(aset ~$scope ~prop-str ~val)))
(defmacro defbinding [$scope prop & body]
(let [prop-str (str prop)]
`(aset ~$scope ~prop-str (fn [] ~@body))))
(defmacro defcontroller [ng injections & body]
(let [inner-f `(fn ~injections ~@body)
array-form (apply array (conj (vec (map str injections)) inner-f))]
`(.controller ~ng ~array-form)))
(-> js/angular
(.module 'todoApp' (array))
(defcontroller [$scope]
(defvar $scope todos (array
(js-obj "text" "learn angular" "done" true)
(js-obj "text" "build an angular app" "done" false)))
(defbinding $scope addTodo
(.push (.-todos $scope) (js-obj "text" (.-todoText $scope) "done" false))
(aset $scope "todoText" false))
(defbinding $scope remaining
(reduce + 0 (filter #(not (.-done %)) (.-todos $scope))))
(defbinding $scope archive
(aset $scope "todos" (filter #(not (.-done %)) (.-todos $scope))))))
Even if you’re not writing your own macros yet, you’ll definitely benefit from someone else’s work. Take a look at things like Korma, an SQL DSL:
(defentity address)
(defentity user
(has-one address))
(select user
(with address)
(fields :firstName :lastName :address.state)
(where {:email "[email protected]"}))
2. Ring
Ring takes something complex and horrible (Java Servlets) and turns it into
something beautiful (pure-functional web programming). You write a function
that takes a request
, represented as a regular Clojure map, and returns a
response
, likewise a map. That’s it; the whole Ring API is the contents of
the requets and response maps:
(require '[ring.adapter.jetty :refer [run-jetty]])
(defn my-handler [request]
{:body "Hello World" :status 200})
(run-jetty my-handler {:port 5000})
Your WHOLE APP is a function that takes a request and returns a response. And, thanks to Clojure being awesome, this approach makes a lot of things easy. For example, routing:
(defn my-router [{uri :uri :as request}]
(condp #(re-matches %2 %1) uri
#"^/$" (index-view request)
#"^/hello" (hello-router request)
(not-found-view request)))
(Note: use a library for this IRL)
Or perhaps, middlewares:
(defn wrap-string-response [handler]
; Return a new handler that wraps the incoming one
(fn [request]
(let [resp (handler request)]
(if (str? resp)
{:body resp :status 200}
resp))))
(run-jetty (wrap-string-response my-handler) {:port 5000})
(Note: Lots of great middlewares already exist)
1. Live coding
This is the real killer feature of any lisp-like language. Sure, lots of your time is spent bolting tedious parts onto other tedious parts. But sometimes, the core of your problem is something really challenging, and this is where exploring your problem with live coding really shines. Other languages have things like this (iPython notebook and MATLAB’s code sections spring to mind), but those languages don’t have the major advantage of being structured like lisp is; thanks to all those parentheses, you can precisely choose what gets evaluated, and that’s a good thing. (The other good thing about parentheses is that they enable some structural editing, but that’s another article).
In fact, live coding has so influenced my work that I always keep LightTable handy, in case I need to work through something tricky in Javascript or Python. In Clojure, I use vim-fireplace, which lets me also use my favorite text editor. I urge you to grab LightTable yourself, if you use any of those languages. I promise you’ll love it, or at least like it.
Well, I’m done gushing. I hope I’ve convinced someone out there to take Clojure for a spin.