Lessons from functional programming

As functional programmers, we don't need to be convinced of the benefits of immutability. We bought into this premise long ago. However, were we not be able to use immutability efficiently, it would not have become commonplace in functional programming languages.

We owe it to the huge amount of research that went into Purely Functional Data Structures, first by Okasaki in his book of the same title[3] and then improved by others.

Without it, our programs would be ballooning, both in space and runtime complexity.

The general idea is that given a data structure, the only way to update it is by creating a copy of it with the desired delta applied:

(conj [1 2 3] 4) ;; [1 2 3 4]

In the following diagram, we have a simplistic view of how conj operates. On the left, you have the underlying data structure, representing the vector we wish to update. On the right, we have the newly created vector, which, as we can see, shares some structure with the previous vector, as well as containing our new item:

In reality, the underlying data structure is a tree and the representation was simplified for the purposes of this book. I highly recommend referring to Okasaki's book should the reader want more details on how purely functional data structures work.

Additionally, these functions are considered pure. That is, it relates every input to a single output and does nothing else. This is, in fact, remarkably similar to how React handles user interfaces.

If we think of a UI as a visual representation of a data structure, which reflects the current state of our application, we can, without too much effort, think of UI updates as simple functions whose input is the application state and the output is a DOM representation.

You'll have noticed I didn't say the output is rendering to the DOM—that would make the function impure, as rendering is clearly a side effect. It would also make it just as slow as the alternatives.

This DOM representation is essentially a tree of DOM nodes that model how your UI should look, and nothing else.

React calls this representation a virtual DOM, and roughly speaking, instead of watching individual bits and pieces of the application state that trigger a DOM re-render upon change, React turns your UI into a function to which you give the whole application state.

When you give this function the newly updated state, React renders that state to the virtual DOM. Remember that the virtual DOM is simply a data structure, so the rendering is extremely fast. Once it's done, React does one of two things:

Without digging into the nuts and bolts of React, this is essentially how it is implemented and the reason it is faster than the alternatives. Conceptually, we could imagine that React hits the refresh button in our browser whenever your application state changes.

Another great benefit is that by thinking of your UI as a function from application state to a virtual DOM, we recover some of the reasoning we're able to do when working with immutable data structures in functional languages.

In the upcoming sections, we will understand why this is a big win for us Clojure developers.