2014-01-27
Reactive programming is taking a ride on the Computer Science pop culture carousel, and attracts an impressive list of corporate sponsors: React (Facebook), Rx (Microsoft), RxJava (Netflix), Elm (Prezi).
In the Clojure world, two libraries getting buzz with reactive cores are Om (built on React) and Hoplon.
Om and a post by Fredrik Dyrkell inspired this binary clock demo in Hoplon.
Hoplon is ambitious, seeking to unify HTML page markup and the JavaScript environment.
For details, check out Hoplon.io - Getting Started and Alan Dipert’s design summary.
So what does it buy us?
(html
(body
(let [current-time (cell (js/Date.))]
(js/setInterval #(reset! current-time (js/Date.)) 1000)
(clock :time (time->model current-time) :legend [8 4 2 1]))))
That’s it. That’s the heart of our demo code; thank you all for coming. :)
These all compose.
html
, body
clock
let
, function literaljs/setInterval
, js/Date
cell
With ClojureScript and Hoplon, HTML is lifted into first class functions. HTML is Lisp.
Javeline is Hoplon’s reactive core.
…a spreadsheet-like dataflow library for managing client state. Hoplon tightly integrates with Javelin to reactively bind DOM elements to the underlying Javelin cell graph.
As with spreadsheet input cells, data flow models start with stem cells.
Our stem cell holds a JavaScript date to represent the current time. Later, we build a graph of formula cells and UI elements that react when input cell values change.
(let [current-time (cell (js/Date.))]
...)
Once per second, update the value in our stem cell.
(js/setInterval #(reset! current-time (js/Date.)) 1000)
The leaves of the graph returned by time-model
are all formula cells that react to changes from their parents and drive the binary clock UI.
(defn n->bits
"number => [[base-10-digit bit-list]+]
e.g 53 => [[5 [false true false false]] [3 [true false false false]]]"
[n]
(mapv (fn [digit]
[digit (mapv #(cell= (bit-test digit %)) [3 2 1 0])])
[(cell= (quot n 10)) (cell= (mod n 10))]))
(defn time->model
"binary clock dataflow, takes a stem cell holding a js/Date"
[time]
{:hours (n->bits (cell= (.getHours time)))
:minutes (n->bits (cell= (.getMinutes time)))
:seconds (n->bits (cell= (.getSeconds time)))})
Formula cells are created with cell=
and react to changes from any lexically-referenced input cell.
To build up our UI, we create four custom elements: led, column, column-pair, clock.
1(defelem led
2 [{:keys [on]} _]
3 (div :do-class (cell= {:unit true :on on :off (not on)})))
4
5(defelem column
6 [{[digit bits] :state} _]
7 (div :class "col"
8 (conj (mapv (partial led :on) bits)
9 (div :class "unit" (text "~{digit}")))))
The Hoplon Getting Started guide covers elements, so a few highlights:
(partial led :on)
(line 8)led
’s and div tag children. (line 8)text
is a macro that does some string interpolation and wires the input cell to the value of a text node. (line 10)on
, digit
- can be simple values or cells. This polymorphism helps separate UI and model development.Finally, the clock element.
(defelem column-pair
[{[msd lsd] :state} _]
(div :class "colpair"
(column :state msd)
(column :state lsd)))
(defelem clock
[{legend :legend {:keys [hours minutes seconds]} :time} _]
(div
(div :class "col legend" (map #(div :class "unit" (str %)) legend))
(column-pair :state hours)
(column-pair :state minutes)
(column-pair :state seconds)))
Hoplon’s goals remind me of Elm’s - unify HTML and program logic with a functional reactive core. Hoplon doesn’t go as far as Elm in that layout and styling are still CSS. But wholesale hiding the underlying runtime is also an expressiveness drawback - it limits what is possible.
Hoplon seems to have found a sweet spot, lifting HTML into Lisp while embracing its host runtime.
With composable UI abstractions comes a challenge to maintain consistent state between components.
One approach is that taken by Om - all state in a ClojureScript atom with components sharing it via cursors.
Dataflow is Hoplon’s story for state consistency. Cells are simples; cell graphs and custom elements compose seamlessly with ClojureScript and Hoplon’s HTML abstractions. There are no protocols to implement or bespoke scoping and access rules, just functions all the way down.
Tim Bray recently lamented the state-of-the-art for building client-side software, and the competing solutions seem more numerous than the problems.
So is this just another over-hyped silver bullet?
Reactive programming is trendy. But like most good ideas in Computer Science, this one is old. The spreadsheet model is declarative, proven, and arguably the most successful UI model in extant.
Thanks to Micha Niskin and my co-worker Alan Dipert for authoring Hoplon and giving feedback on this article. Thanks to Fredrik Dyrkell for writing the ClojureScript/React/Om binary clock.
INRIA Reactive Programming
Alan Kay on CS pop culture
Facebook React
Microsoft Rx
Netflix RxJava
Elm Language
Om source
ClojureScript/React/Om Binary Clock
Hoplon.io
Hoplon.io: Getting Started
Hoplon design summary
Javelin
Software in 2014 - Tim Bray
Reactive Manifesto
LANPAR Spreadsheet
Related tags:
email comments to paul@bauer.codes