Why Om and Pedestal?
I asked a good friend of mine to review my post on route naming in Pedestal. He’s not familiar with Clojure and said that he wanted to hear about how I chose Om and Pedestal. I had considered including a summary of these choices, but it felt out of place, detracting from the main point of how I worked through a development problem. While I don’t think my reasons for choosing these libraries are novel, I think they’re worth noting, so from the perspective of someone relatively new to web application development in Clojure, I’m noting them here.
First off, I like Clojure. Immutability, dynamic but strong data typing, simple syntax, Java interop providing access to the wide array of battle-tested Java libraries, strong concurrency primitives—all great stuff. ClojureScript brings a lot of this goodness to JavaScript runtimes, along with taking advantage of the Google Closure compiler which includes dead code elimination as well as your everyday minification. So I’m predisposed to looking for client and server libraries for Clojure.
Why Om?
React is a remarkable web UI library. Among its other features, it introduces a virtual DOM updated by the application. A separate rendering module propagates these changes to the DOM only when the browser wants to redisplay the page. I find this decoupling of logic from display compelling. Application speed is no longer a function of how fast the page can be redrawn.
The primary drawback is that it’s JavaScript. Don’t get me wrong, the language has a lot going for it. Up until ES6, the syntax was quite limited and simple. Function scope can take some getting used to. And while prototype inheritance can be a unusual for those coming from more common object inheritance models, you can choose to avoid inheritance. Having functions as first class objects makes functional programming straightforward.
That said, I find structuring Javascript applications more difficult than I’d like. Callback hell can make reading code difficult and following application logic painful. ES6 has features which address some of this but at the cost of introducing new syntax which in turn adds its own cognitive load.
Om is a ClojureScript interface to React. David Nolen has spent a great deal of time thinking about the problems React, GraphQL,, and Falcor are trying to solve, and applied a healthy dose of Clojure. David discusses his motivations for Om Next, the second interation of Om, on Episode 093 of the Cognicast podcast.
Why Pedestal?
I see three front-running web server options in Clojure: Ring, HTTP Kit, and Pedestal.
Ring is an interface for writing web server applications as servlets. It follows on the tradition of WSGI in Python and Rack in Ruby. An incoming request is treated as an argument to a handler function, and the response is the function’s return value. Middleware features (such as authentication on the way in or compression on the way out) are functions wrapped around this base handler function. There are a large number of Ring middleware libraries which address a lot common needs. Ring is also single-threaded and blocking. A long-running request will block the web server instance.
HTTP Kit addresses the blocking issue by using an event-driven model to support asynchronous concurrency. It’s also Ring compliant so it can take advantage of Ring middleware libraries. Node.js and Nginx are both examples of how performant asynchonous event-driven concurrency can be. HTTP Kit has been shown to support up to 600K concurrent connections.
However, neither Ring nor HTTP Kit take advantage of one of the benefits of Clojure on the Java virtual machine. The JVM has a very solid thread implementation, making it easy to handle multiple requests, and Clojure’s concurrency models make it simple to avoid the pitfalls of thread synchronization. With multi-core machines the norm, why not use them?
Pedestal also has a different approach to request handling. Rather than wrap functions around functions, interceptors are inserted in an execution queue, each interceptor intercepting the inbound request and the request context on its way to the handler. The response returned by the handler is passed back up through the execution queue where it’s again processed by the interceptor chain. At each stage along the queue this context is just data, which can be potentially moved from one thread to another. The request is decoupled from the thread of execution.
And while Pedestal isn’t Ring compliant, the developers recognize that the value
in the existing Ring middleware ecosystem and have made it possible to use
Ring middleware as interceptors in the interceptor chain. Also,
Pedestal request maps are similar to Ring: The ring.util.response
namespace
is even required in the
Leiningen Pedestal service template. Pedestal
provides the library benefits of Ring and the concurrency benefits of Clojure
on the JVM. I find that quite compelling.
Summary
So there you have it. It’s still very early in my experience with Pedestal and Om. I want to stay aware of the investment I’m putting into learning these new tools and hope to avoid succumbing to the sunk cost fallacy. And who knows? A year or two from now I may look back and wonder What was I thinking? Well, this blog post will be here to answer that.