What I've Learned About Making ClojureScript Applications With Pedestal

Posted on by Tero Parviainen (@teropa)

Pedestal is an interesting beast. It aims to provide tools for building single-page ClojureScript webapps with the simplicity and elegance we've become accustomed to in Clojure. It is also perceived as a difficult technology to master with several new concepts to grasp, some of them quite subtle.

I think there's some truth in both claims. In this post I'll share my experiences with Pedestal so far. It is loosely based on a talk I gave at the ClojuTre event earlier this fall.

The Promise of ClojureScript

I've been playing with ClojureScript since the very early days, and even wrote one of the early Hiccup ports back when there were no idiomatic rendering libraries. I love Clojure and the prospect of using it for single-page app development in the browser felt attractive. It still does. Web GUI development is a messy job. If you could bring to it even a fraction of the simplification Clojure has given us on the server, that would be a big win.

However, I haven't really been able to make myself comfortable doing substantial ClojureScript applications. I've tried several different approaches, ranging from heavy use of the Google Closure libraries to combining the various ClojureScript micro-libraries available. But I just haven't gotten anywhere close the productivity and flow I have with plain old JavaScript.

I think I understand now what my problem has been: Since there hasn't been anything that provides an overall architectural backbone for ClojureScript apps, I've been attempting to replicate the way I do things in JavaScript. Mostly that means coming up with designs influenced by the JavaScript flavor of object-orientation. That's quite different from idiomatic Clojure, and thus it has felt like working against the tools at hand instead of playing to their strengths.

Pedestal

When Pedestal was announced, I was excited about the prospect. Here was something that promised to provide the architectural backbone I was looking for, and to do it in a way that embraces what Clojure is all about: Functional code, data flow over control flow, and the simple over the easy.

Although the general idea of Pedestal was exciting, it was really the following piece of code in the documentation that hooked me in:

(ns hello-world
  (:require [domina :as dom]))

(def application-state (atom 0))

(defn render [old-state new-state]
  (dom/destroy-children! (dom/by-id "content"))
  (dom/append! (dom/by-id "content")
               (str "

" new-state " Hello Worlds

"))) (add-watch application-state :app-watcher (fn [key reference old-state new-state] (render old-state new-state))) (defn receive-input [] (swap! application-state inc) (.setTimeout js/window #(receive-input) 3000)) (defn ^:export main [] (receive-input))

What's going on in this example is that the application state is held in an atom, and transformed to the next value using swap! whenever something happens. A watch added to the atom triggers a render call when the value changes. The rendering function is given both the old state and the new state, and it renders whatever needs to be rendered based on the change.

What I particularly liked in this example was the decoupling of event handling from rendering, and the whole idea of storing the state of the app in an atom. It was also nice that the whole thing was done in just a few lines of idiomatic Clojure.

Now, here's what the documentation said next:

If you have ever built a ClojureScript application, this is the way you start.

The thing is, that's precisely not the way I had been starting. Instead I had been fumbling around trying to replicate JavaScript architectures in ClojureScript. So I thought if this is how someone approaches ClojureScript app development, maybe their library is worth looking at.

What Pedestal Is

The first thing to understand about Pedestal is that it's actually an umbrella term for two completely separate things: Pedestal Services and Pedestal Applications. The Services project is a serverside toolkit for writing HTTP web services in Clojure. The Applications project is a client-side technology for writing ClojureScript applications. The two are completely orthogonal and you don't need one to use the other. There's also no special "integration" between the two to speak of. You can just as well serve a native iPhone application from a Pedestal service, or consume APIs built on Node.js from a Pedestal application.

In this post I'm restricting the scope of the discussion to Pedestal Applications. While Pedestal Services can probably be very useful for writing scalable, asynchronous web services, I'm particularly interested in the single-page webapp problem and that's pretty much backend-agnostic.

Is It A Library or A Framework?

In the Clojure community there's a strong preference towards using small, specialized libraries instead of large frameworks (think Ring+Compojure+Korma vs. Ruby on Rails). Based on what I've heard from several people, many are slightly put off by Pedestal because it looks like a large framework. Let's explore this a bit.

When you create a Pedestal application using the Leiningen template, you get a lot of stuff, at least by Clojure standards. The sheer footprint of the generated project gives an impression of something big an unwieldy. The thing is, though, that what you get is mostly either tooling for making development tasks easier, or placeholder files for the kind of project structure the Pedestal team sees fit. Most of it, in other words, is incidental to the core of a Pedestal application itself.

Pedestal is described as a "tool set" and not a framework. "Tool set" is in many ways an apt term: It's a collection of interconnected small libraries and tools, from which you can pick and choose. For example, I wrote the Clojure Cup live feed mini-app with Pedestal, but instead of using Pedestal's rendering facilities, rendered the app using a bit of Dommy scripting.

There is one aspect in which Pedestal is definitely more "frameworky" than most Clojure libraries: There's a lot of inversion of control going on. You, as the application developer, write pieces of code which Pedestal invokes for you at different times. You don't really see the whole control flow of your application, because much of it is handled by Pedestal. This does not come with as big of a cost as often happens in OO (inheriting from framework classes, requiring the framework to run tests, etc.), but it does make it slightly more difficult to understand what's going on at times.

Web Application Development with Verbs

At the heart of pedestal applications are three models for application state: The data model, the application model, and the document object model (DOM).

The data model and the DOM are roughly equivalent to what most MV* frameworks have: A separation of domain data and user interface. The application model, on the other hand, is a new concept. You could describe it as a projection of the data model to the shape of the DOM, or as an intermediate step between those two models. It contains exactly what the UI will display, in the order in which the UI will display it, but omits concrete UI concerns such as specific HTML elements or JavaScript events. This is one of the more difficult things to learn about Pedestal, but it is arguably a very Clojure thing to do: Aggressive separation of concerns by decomplecting the structure of the UI from the specific mechanics of rendering it.

The biggest realization for me, however, has been that the way you define these three models in Pedestal is different from what is typical in OO-centric frameworks. You do not specify the models with schemas or classes, but instead you specify the functions that produce those models. That is, you provide the verbs, not the nouns. This definitely makes sense in a functional setting, but it still took some time for me to grock.

The Data Model: Transforms

The data model is an arbitrary data structure that contains the data of your application, in any shape that makes sense. It does not deal with any UI concerns.

Here's what a data model could look like in a To-Do application:

{:todos ["Wash dog" "Brush cat" "Address wounds"]}

Instead of defining the data model explicitly, you define the transform functions that produce the data model when given messages about changes in the world (e.g. data received from server, user action).

For example, the To-Do application could define that an :add-item message received to the :todos address ("topic" in Pedestal parlance) should add a new item to the collection of To-Dos. The following snippet in the application specification defines such a transformation:

[:add-item [:todos] add-transform]

This references a generic transformation function add-transform. That function will receive the old value and the message received, and return the new value:

(defn add-transform [old-value msg]
  ((fnil conj []) old-value (:value msg)))

With these pieces in place, the following messages received by the application would produce the data structure with three To-Do items:

{msg/topic [:todos], msg/type :add-item, :value "Wash dog"}
{msg/topic [:todos], msg/type :add-item, :value "Brush cat"}
{msg/topic [:todos], msg/type :add-item, :value "Address wounds"}

We never defined that there is a map with a :todos key, whose value is a vector of the todos. Instead, we define the incoming messages and the corresponding transformation functions. The data model is derived by Pedestal from them.

The Application Model: Data flow

The projection of the data model to the application model follows a similar pattern. The application model follows the shape of the DOM, and is always a tree structure (since that's what the DOM is). It can be more complex than the data model, but doesn't necessarily have to be. For example, the To-Do application model could look like this:

{:main {:todos ["one" "two" "three"]}}

In this example, the data is scoped under a :main key. That might correspond to the main screen of a multi-screen application. That's the only difference in the application model to the data model in this case. In other applications the application model could be wildly different from the data model - it all depends on the needs of the UI.

The way you produce the application model from the data model is by defining one or more emit functions. They react to changes in the data model, and produce deltas that describe changes to the application model tree: New or removed nodes, or changed node contents. For the To-Do app example, the following emit specification would do the trick:

[#{[:todos]} (app/default-emitter [:main])]

What this says is: "Emit everything from :todos in the data model under the :main key of the application model, using Pedestal's default emitter function".

Again, we don't specify the application model tree. We merely specify the process by which the application model comes to be. In fact, the application model does not even exist as a concrete data structure in a typical Pedestal application. It is just a conceptual overlay for the sequence of deltas that we emit from our data model. In our example, the following deltas would be produced:

[:node-create []             :map]
[:node-create [:main]        :map]
[:node-create [:main :todos] :map]
[:value       [:main :todos] nil ["one" "two" "three"]]

These deltas create the three nodes of the application model tree first: The root node, the root -> :main node, and the root -> :main -> :todos node. The final delta assigns the value of the root -> :main -> :todos node.

The "application-model-as-a-sequence-of-deltas" idea is a clever one, and admittedly slightly difficult to think about at first. But it does provide ideal fodder for the rendering code:

The Document Object Model: Rendering

The final piece of the puzzle is the DOM - the actual structure of HTML elements that the browser displays. It is produced from the application model using a set of render functions. Each render function is triggered by particular deltas in the application model. Once again we're dealing with the process of creating the DOM more than specifying the structure of the DOM.

The rendering of the To-Do app could be hooked up to the deltas as follows:

Hooking this up is just a matter of telling Pedestal which functions should be called from which deltas in the application model:

[[:node-create    []             render-app]
 [:node-create    [:main]        render-main-page]
 [:node-create    [:main :todos] render-todo-list]
 [:value          [:main :todos] render-todo-items]
 [:node-desroy    [:main :todos] remove-todo-list]
 [:node-destroy   [:main]        remove-main-page]
 [:node-destroy   []             remove-app]]

I'm omitting the actual render-* functions, but they can basically just make changes to the web page's DOM tree based on their arguments. To achieve that they may use Pedestal's built-in rendering and templating facilities or some other libraries.

Further Reading

The best way to really get to know Pedestal is to go through the official tutorial. It addresses all the different aspects of Pedestals – not only the Applications but also the Services.

The tutorial is very thorough, and as such slightly reinforces the idea of Pedestal as a large, monolithic technology, at least until you actually go through it all. I hope to address this in the near future by providing a series of small, focused tutorials on specific features of Pedestal that you can both learn and use in isolation.

Know Your AngularJS Inside Out

Build Your Own AngularJS

Build Your Own AngularJS helps you understand everything there is to understand about AngularJS (1.x). By creating your very own implementation of AngularJS piece by piece, you gain deep insight into what makes this framework tick. Say goodbye to fixing problems by trial and error and hello to reasoning your way through them

eBook Available Now

comments powered by Disqus