If you like this article, check out my work to solve personal finance at fiskal.app.
FRP tries to solve the problem of asynchronous work, event propagation and data transforms that happen as a result of events about your code base. Basically you create an observable that reacts when an event is triggered. The trigger could be a UI event, like on click, or a network request or other things. Each trigger returns a stream that you can chain (ala jQuery) to change or combine other streams together. ReactiveX group does a much better job explaining what they do. Their site is really great at explaining FRP http://reactivex.io/.
RxJS and FRP in its prime
RxJS does a lot of things really great. It allows you to create custom event streams that can have multiple listeners on the output. The observables model and chaining transforms are very useful and help keep related pieces of code together. Observables create stream that can be combined with other streams. It takes away the global events and keeps them localized to where your code needs the to react to the events. If you’ve used Mutation Observers or data binding or Backbone events in JS or KVO or NSNotificationCenter in Objective-C, RxJS like that but much more powerful. RxJS elevates data flow to be a first class architectural pattern on the level of MVC.
RxJS elevates data flow to be a first class architectural pattern on the level of MVC.
So why did I move on from FRP? I spent 4 months with 8 hours a day of coding with FRP. I’m no expert but I hit some limitations that caused me to look to see what else was out there. It might just be that I have just not used FRP “properly” and I would love to understand it better.
In my app I had streams for network requests that transform server responses to a structure that was more useful for the UI. There were also observers on UI events. FRP has a very unique API that takes a bit to learn, even if you’ve done map/reduces before. The DSL being very unique made it harder to reuse other functions in my code base. I couldn’t reuse code but the Observable and Streams could be pasted around to leverage in other classes.
Merging and combining streams is where it got muddy really fast. I had events going that were hard to track down. I lost the finer grain control when more than one stream was being used. I’m fully aware this could be my misunderstanding about how to use FRP. At the same time, the methods a library exposes guide developers about to best use them. Take for instance Backbone’s vs. Angular’s API. Backbone is every open and easy to do things outside the “intended” use; when in Angular doing things outside the intended patterns are much more difficult. API design guide developers into certain patterns.
In the end, it was harder to reason about everything going on. Debugging FRP processes was stepping through a lot of the internals of the libraries to find where events got twisted. Using FRP in smaller more isolated uses and limiting combining streams was much more beneficial and the file size increase if you’re just using it in place of a single callback function is not very useful. FRP hits the middle ground between these very well for me.
A simpler async and data flow
Go lang and CSP is really cool, right? Async work looks like sync work and all the code is together making it easy to rationalize about. But I can’t use that in my web app. That’s where ClojureScript comes in. Their community is known for just using great ideas from other programming languages. ClojureScript’s core.async is fabulous. They basically stole the best parts of Go and FRP but with an API that’s super simple. Core.async feels like programming 5 years in the future. Core async has Go channel, that “park” to make async code look like sync code and on top of that they’ve added transducers. Transducers are these awesome things to take a bunch of abstract functions that do minor task and quickly compose them together and attach them to a channel.
[ClojureScript’s] core.async feels like programming 5 years in the future.
Breaking down ClojureScript
ClojureScript’s Transducers replaced FRP’s the multiple flatMap and take functions. Transducers are just stupid little functions that can be reused in regular map/reduce/filter operations. In contrast to FRP functions used inside streams needed to understand they were in a stream. Functions used in a transducer don’t know they’re in a transducer. Basically I can reuse them with all my current map/reduce/filtering code inside async channel.
ClojureScript’s analogous structure to FRP Observables are Atoms and Atom watchers. In ClojureScript all the data is immutable. When you first hear that is sounds insane but watch Rich Hickey’s Simple made Easy and you’ll see how awesome it is to have immutable data the default option. Atoms are ClojureScript’s mutable data. Using watchers, you can have a single callback function or channel trigger when a piece of data changes.
The last pattern that we heavily use is ClojureScript version of classes. ClojureScript is a functional programming language but they’ve seen the value that OOP languages provide. When it comes to encapsulating dependancies OOP classes are very useful. ClojureScript handles this with Stuart Sierra’s Component library. It really helps control a lot of dependancies in our data flow.
ClojureScript is the Triforce of Prower
In the 3 weeks it took to build our ClojureScript app I have never learned much, grew so much as a developer and understood why my OO code had bugs and how to control them. Writing ClojureScript, learning the syntax, patterns, paradigms and ideas from the ClojureScript community has been invaluable. If you’re just using ClojureScript-inspired patterns in another language, there are huge concepts you’ll miss out on. Functional programming is the future (If you look closely in The Martian, their spaceship is coded in a LISP).
ClojureScript’s Prowess is transparent bridging to other languages
There are tons of great UI developers that can build great work with React and JS. 85% of our development time for our web app at Capital One is building UI. We don’t need a huge barrier for entry to get great developers doing great work in our web app. With ClojureScript they don’t even need to know they are using ClojureScript.
Inside of ClojureScript we use core.async, go channels, transducers, immutable data, components, atoms and atom-watchers. We can take full use of the prowess of ClojureScript. When we want to talk to the JS world we have one single file called export. It puts a JS interface on top everything. When you’re in the JS world you call a function that returns a promise that give you mutable JSON objects. This is our entire code to transform CLJS patterns to JS patterns and allow for interop.
ClojureScript is well worth it for anyone who wants learn. The community is great. The patterns I’ve learned give me a deeper understanding about alternate ways to write code. I have less bugs, less refactors and easier refactors of code. Our ClojureScript library has been pretty flawless with only a couple bugs in production. React has had an order of magnitude less bugs compared to my time doing Backbone and iOS apps. ClojureScript had a order of magnitude less bugs than React.
There was a lot more discussion about this topic than I expected. I wanted to clarify a point about my recommendation of ClojureScript. The only reason I don’t fully recommend ClojureScript for everything is because of the barrier to entry. But for the effort it takes to learn, ClojureScript pays off in spades.
There are many in the React community say they’ve been inspired by David Nolen and the wider ClojureScript community. It turns out I’m that guy in the back of the room pushing them to take the next step and really learn ClojureScript. It’s the entirely of ClojureScript can’t be matched by taking parts and converting them to another language. I hope the more people understand CSP, functional programming and transducers the more it inspires them to learn ClojureScript. To make an analogy to 80’s cartoons, you can have the red, black, green, yellow and blue lions all separately but just think what you could do if you started out the episode out with Voltron?
As for FRP, it’s great and is a very useful and practical pattern for data flow management. They have a lot of great ideas and it’s great to see how they make data flow a first class citizen. The ReactiveX group (http://reactivex.io/) has some wonderful documentation I wish I had a year ago. There’s also a insightful response to my post on Hacker New (https://news.ycombinator.com/item?id=10752742) that I recommend you read to get a more nuanced perspective of RxJS.
There are now two more posts that go more in depth about how we use ClojureScript in our single page web app.
- Using ClojureScript from vanilla JS and React (http://bit.ly/fluxless).
- How we structure of our ClojureScript data layer (http://bit.ly/triforce-of-power). Atoms and atoms watchers have mostly replaced the things I really loved about FRP.
Appendix: ClojureScript resources
- If you do nothing else read this book: https://www.niwi.nz/cljs-workshop/#_introduction_to_core_async
- Installing CLJS and using figwheel/lein in the browser to start coding: https://github.com/bhauman/lein-figwheel/wiki/Quick-Start
- Clojure docs are invaluable for figuring out how to do something: http://cljs.info/cheatsheet/ and http://clojuredocs.org/
- Rich Hickey’s “Simple made Easy” flat out the best talk I’ve ever heard: https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/SimpleMadeEasy.md
- Libs I highly recommend: https://github.com/Prismatic/schema, https://github.com/r0man/cljs-http, https://github.com/andrewmcveigh/cljs-time, https://github.com/stuartsierra/component