d3 Methods as Data in ClojureScript
In the debut of my blog, I’d like to highlight the methods I’ve been learning through studying functional program design patterns. This post specifically shows the influence of Clojure’s “it’s all data” methodology while also giving some insight into the power of the language in the browser.
I’ve had the pleasure of working with the visualization library d3.js this month and the last for work and personal projects alike. While some libraries exist to use d3 in ClojureScript, such as c2 and strokes, I thought it would be interesting to try my own implementation.
Starting out
d3 uses method chaining to output its results. For example, we can select an element and input a dataset while specifying which child elements to use and their properties.
Here’s an example of using ClojureScript to do the above:
Of course, there’s an easier way:
While this method works, it doesn’t do much to take advantage of Clojure’s powerful tools to transform data. One of the perks of chaining methods is the ability to apply them all at once with a single return value - the drawback being, however, the inability to revisit the operation as a whole, make comparisons, or even add or remove operations at will.
As an exercise, I wrote a small wrapper around the interopability to input a persistent data structure and ouput a chaining object. The first step was defining some boilerplate functions. The chain
inputs a JavaScript object and sets up field access by keywords.
So a lot like how we can do this in JavaScript,
we can now use keywords to do so in ClojureScript:
This does have a bit of a kink to it:
While a programmer wouldn’t be fond of writing this, it does have a subtle property, namely that at any point in time we can pass in different object with the same function protocols as d3.js.
Extending
An invaluable lesson I’ve learned while using Clojure is the effectiveness of extensibility. In order to abstract our chaining, there’s a wonderful set of tools that give us the ability to do so elegantly.
By creating a new type, we can be specific about how we implement our abstraction. The res
variable will simply be the result of our method chain.
We can further extend our type, even into the language’s own protocols. I like the way keys in Clojure are functions themselves when called. We’ll use this protocol, called ILookup
to extend the Grapher type.
We’re now able to access our d3 functions as keywords.
As you can see, the return object is another Grapher, and we can realize its results. It would be more convenient to call these functions directly after finding them.
Reducing
We’re almost in business. We can now find and call our functions with relative ease, but we’re stuck writing conj
-like syntax.
It still looks a lot like the verbose example above, but there’s a key difference. Since we brought up conj
, let’s take create a function that inputs a vector and a grapher and outputs a Grapher.
Now we can do something closer to this:
We’ve translated our instructions to Clojure vectors. Wait, if that’s the case, why don’t we take some hints from conj
?
Now we’re talking. Our instruction set is merely a list of lists, a lot like the language it was written in. We’re now free to make comparisons, add or remove methods, or even write higher functions that create the instruction set for us based on our goals.
Conclusion & Implications
There are implications to the methods described above. One of the big disadvantages is the performance loss in selects
. Even small benchmarks showed disappointing results:
This is a rare use case of d3, however. Selecting elements is trivial compared to an operation such as the one below:
Not bad. The ClojureScript implementation is a tad behind, but it seems to keep up with large data sets. After you :enter
into an element– which in short “binds” data to nodes– the performance normalizes.
Creating the methods above was a great way to wrap my head around chains. While I’m certain there are more efficient ways to achieve these results, I was impressed by how easy it was to implement Clojure’s core protocols to extend my own types right in the browser.
You can check out the code on Github. I plan on extending this implementation further to reflect other work such as c2’s ability to declaritvely create HTML blocks. Another post will certainly be in order.