ClojureScript is the Triforce of Power

A while ago my 8 year old daughter was getting frustrated trying to alphabetize her spelling list. She’s constantly misplacing of the word and starting all over again. Being an opportunistic father, I told her I’d teach her how to tell the computer to do it for her. We opened up a ClojureScript REPL and she wrote her first line of code (sort [“truth” “simple” “powerful” “learn” “happy”]). If an 8 year old can learn basic ClojureScript so can you.

On Capital One’s Level Money team we just use ClojureScript for our front-end JavaScript single page web app. React handles our view layer (bit.ly/fluxless) and ClojureScript handles the data layer. Our ClojureScript library is called Triforce. If you want to see how our Javascript interacts with ClojureScript, check out our other post. ClojureScript is amazing at handling asynchronous work, dependency management, optionally does static type checking, event propagation, data manipulation, writes reusable/refactorable code, low memory footprint and is very performant. First up:

Data Fetching

That’s it. If you want to enable CORS just add a simple Object (called a “map” in ClojureScript).

Async

Async work in ClojureScript is a joy. There’s no callback hell or wrapping data in Promises. ClojureScript uses GoLang’s CSP for async. It takes normal synchronous looking code and just parks until that line is done executing. The core construct used for parking is a channel. By default a channel has one piece of data on it and the other side has to “park” there’s something on the channel to take. put! will put data on a channel and take! will take it off (>! is synonymous for put! and <! is for take!). The last concept to understand is async work can only happen inside a go block, which tell us that any code in here does not have to be synchronous.

So in the code above, it parks until it can take! the response the calls the print function to show the response. When we want the response to be set to a variable we just call let and then a variable name. We can rewrite it like this:

REST calls

Unfortunately we don’t have a shiny GraphQL or Datomic back-end. We have standard REST endpoints which work just fine. The ClojureScript way to do things is to pass data around to other functions. So for each endpoint we do just that. We’re create a simple map that details the minor differences of each endpoint.

defn creates a function called “get-repos” with required parameters of hostname, auth and request. Then it calls another function called “call-network” with what we need to for this endpoint.

Now we’ll make a few tweaks to make our network call agnostic. This composability is one of the key features that makes ClojureScript so easy to refactor and reuse; Write less code and change that code quickly.

Static typing in a loosely typed world

So all that should be fairly simple to do but there a lot that is unspoken here. What is request? What is auth? We can’t have those ambiguities on our public interfaces. We’ll add a library from Prismatic called Schema to give us opt-in static typing when it’s really needed. We use static typing on the edge of our namespaces where additional clarity becomes extremely important. It also doubles as validation when the code runs.

To conclude our network layer, we’ll update our http call to be a little more dynamic. We deconstruct the function parameters with the {:keys } syntax and then merge the default-params with the custom params for the call. Also notice that the last line of any function is the return value.

Dependencies

Dependency management isn’t a strong suit of most pure functional languages. In this case, ClojureScript has taken the idea of Classes in OO languages to control dependences better. Internally it’s just a closure in a function. This gives the power of encapsulation with the trade-offs classes can have. At the top level, we express what are our dependencies and how they relate. Then we inject them into components to create a system.

Next we take the components and put them into a system that manages the hierarchy of dependencies.

Event Propagation

The last core piece we use is localized caching and event proration. This is important to save data locally to be reused and modified along with the network requests. When the data is changed in any way we need to expose an interface to allow other functions to be triggered as a response to a data change.

For this we use atoms (mutable data) and atom-watchers.

CLJ->JS

To expose all of this to the JavaScript world we use ^:export on a single function and we wrap the core.async and immutable data in a Promise with mutable JSON.

Small, simple code is more stable and flexible

ClojureScript’s immutable data and focus on small, simple, pure functions make it very easy to compose and understand what the code is doing. We also handle data transforms and other work locally in isolated functions with just standard ClojureScript map/reduce type functions. ClojureScript has a steep learning curve for those with only an object-oriented background. ClojureScript has kept our View layer in sync while only exposing what’s needed. We’re excited for DataScript, OM Next reconciler and Datomic to make things easier and more flexible for our Data layer running in the browser and on Node.

ClojureScript is a fantastic language! To get up and running fast, check out figwheel, read the online ClojureScript book and use the ClojureDocs examples when you get stuck.


One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.