My friend Peter, the inventor of the Yome (pictured above) and proprietor of Red Sky Shelters asked me to update an interactive widget that I built for him 9 years ago.
This widget represents a blueprint that helps the potential Yome buyer show Peter where they want their windows and doors placed.
It’s all about cognitive overhead
Straightforward: not complicated and easy to understand
As developers we do something that is very difficult. In response to this difficulty we create and reach for tools that promise to take some of this burden from our shoulders. Often we are faced with the hard reality that these tools may not, in fact, be helping us. These tools often offer the promise of lightening our cognitive overhead but in the end, the set of complex problems has just been moved from one location to another. Even worse, the set of problems has been exacerbated with much increased complexity.
As of late, I have been hearing what seem like justified reactions to this complexity. Folks are saying things like: jQuery considered harmful, “Frameworks are dead”, Monolith first etc. There seems to be a reductionism at hand and it’s understandable. For a really great talk about this see Programming with Hand Tools.
The main point here is we actually want to reduce cognitive overhead, and not just push it around, or worse, grow it with bad trade-offs.
There really is no free lunch, so instead of looking to techniques, patterns, libraries, and frameworks, maybe we should look at simplifying the computation itself.
The trouble with Objects
In our code, complexity normally takes the form of Object instances that maintain a mutable local state. I conjecture that Ruby, Java and the general prevalence of OOP has led many developers to code in an object first manner. When faced with a blank file and a problem, the modern developer seems to gravitate towards creating Objects.
It’s this predilection that I take issue with. Folks are unconsciously and prematurely spending the complexity budget of their applications. Eventually there will be a real need, performance or otherwise, to create an Object. I’m suggesting that one wait until that need arises and code in an object last manner.
Why is functional programming straightforward?
My answer to this is that the complexity and cognitive overhead of your program grows exponentially as you add more local mutable state and side effects. When you reason about your program you have to account for the potential state of all these changeable/changing entities and entity systems. It is amazing how quickly things grow past our ability to effectively reason about them. This, my friend, is cognitive overhead.
But in order to write a program we have to compute, right? Where does that leave us?
If we are ruthlessly reductive and eschew all mutable local state and side effects, it leaves us with the pure function:
view = F(state)
This is the very base of computation, and it’s very hard to simplify past this point. (Declarative programming does this, but …)
Using functions we can add code to our programs without significantly increasing the complexity. Programming with pure functions is more like taking a relaxing stroll because you can often limit the scope of your thinking to the input and output of the current function.
Of course, pure functions can only take us so far in an interactive program. State and views are required to change over time in response to user actions. But I am going to ruthlessly stick to pure functions as far as I can, and when I have to compromise and write a function with side effects, I will. In doing so, I will minimize complexity as much as possible and then hopefully the only complexity I have left will represent the essential complexity of the program.
Straightforwardness != Familiarity
It’s important to note that straightforwardness has nothing to do with familiarity. Just because a technique, language or syntax is familiar or even an accepted industry best practice does not mean that it is straightforward. And likewise, just because someone doesn’t understand the code or finds it strange, that does not mean that it isn’t straightforward.
YAGNI - You really aren’t going to need it
I’m not only going to be reductive with my computation but my tooling as well.
As I go through the widget implementation below please notice all the things that aren’t there:
- no jQuery
- no drawing library
- no explicit observable model objects
- no double bindings
- no top level React component
- no custom React elements
- no modules
- no Webpack, Browserfy, less or sass, etc.
Why use React?
If I’m being so reductive why even use React, which introduces its own cognitive overhead? I see this not only as a necessary trade-off, but the very thing that enables us to express views and DOM state functionally.
The DOM API doesn’t let us express
view = F(state) in an efficient
manner. React makes this possible.
For example, with React/JSX I can write view code like this:
These are pure functions that take data and return a Virtual DOM which then can be diffed with the actual DOM and rendered very efficiently. In fact, React is an extremely exciting development and one of the reasons that I’m writing this post in the first place.
To really appreciate this way of coding, it’s important to experience the process as well as the code. For this reason, I am going to guide the reader through the construction of the widget, in a way that simulates how I built it. If you follow along you will get to experience the “live” interactive aspect of the development process as well as appreciate how directly the code applies to the problem.
npm install --global babel
Create the project files and directories:
mkdir -p yome_widget/src mkdir -p yome_widget/build touch yome_widget/src/yome.js touch yome_widget/index.html touch yome_widget/style.css
Grab this content for the
Grab this content for the
We will need the Babel compiler to watch our
yome.js file and
compile it whenever we hit save. We can do this by invoking
cd yome_widget babel src/ -w --out-dir build/
Now, open your browser to
file:///<the path on your
system>/yome_widget/index.html and open
your favorite text editor.
From now on, you can add code to the file by appending the examples
in this post to the
If you would rather follow along with the completed project you can get it from the github repositiory.
The future is live
As a ClojureScripter, I have become very accustomed to live code reloading. When I wrote this code, I made a simple file reloader so that I could see my code changes in real time without reloading the page for every change.
One of the benefits of using pure function definitions and constraining side effects is that it makes it very easy to write code that can just be reloaded “whole hog”.
Go ahead and put this at the top of the
Now, reload the
index.html page and open the Developers Console
of your browser. You should see
--- reloading --- get printed out
every 3 seconds.
The interesting thing here, is that this Reloader code is being reloaded itself, so you can change it and see the behavior change without reloading the browser.
Go ahead and modify the
Reloader.startReloading function and change
the timeout from
3000. Or change the
"--- reloading ---"
"--- reloading files ---". You will see a likewise change of
behavior in the console. You can adjust the timing as you prefer.
This can be a little hard to get used to, but remember that as you make your changes below, you don’t have to reload the browser.
If this isn’t for you or if you need to turn it off, just
remove the code above or comment out the
startReloading line like
Welcome to instantaneous live reloading, it is the future.
Now append these definitions to the
In the code above, you can see that I define a Yome object literal and
on that literal I add an
In this yome widget there will be one state map/object. This will be the central source of truth. The visualization of the state widget will be a function of this data.
Yomes come in three sizes: 6, 7 or 8 sides. Thus, the view from above
is either a hexagon, septagon or octagon. As you can see from the
initialState function above, we are going to be starting with an
The widget allows for drawing different Yome sizes so we will need to
assess the current size often.
Yome.sideCount will allow us to do
this. Notice that
Yome.sideCount is pure function and is not
referencing some local/global state, but rather the state is passed
Now you can check the functioning of
sideCount by uncommenting the log
line below it, then observing the output of the function call in the
Dev Console. This output should be
8. After verifying that it works,
comment out that log line again.
Since we are working with regular
polygons, I am going to
constantly be referencing the angle of one of the polygon slices (an
octagon has eight slices). The
sliceTheta function gives us this
angle in radians.
Again, you can see it working by uncommenting the log line below it and seeing the output in the Dev Console.
I will continue to place these log comments in the code and I will leave it up to you to try them out by uncommenting and recommenting them.
Now, I am planning on having a 500x500 SVG based widget and I’m going to be drawing things arranged in a circle. It will be much simpler to think in terms of radial points. In this case, a radial point is the combination of a distance from a center and an angle from a line pointing straight down.
In order to facilitate radial points it’s necessary to
point about the center at 0,0.
radialPoint function just takes a point that lays straight down
radius from the center and then rotates it to an angle
theta from there.
With this new found ability to create points about a center at any given angle it becomes trivial to generate a set of points that delineate the sides of the yome.
Here we take a set of points and turn it into a string for the SVG polygon element.
If you have been using the log utility
l to see the output of these
functions, then you may be starting to appreciate how easy it is to
verify that these functions are working.
This easy verifiability is also a result of using pure functions. It is often very simple to meet the data shape needs of a pure function by conjuring up some quick data to pass into it.
Here we start generating some SVG with JSX. The
will take the current state and emit an React polygon element.
Now when we get here, we would really like to know if our code for drawing octagons is working. While our log function is helpful for seeing how data-based functions are behaving, it is useless for checking how our visual functions are working.
Verifying graphical functions
You may have noticed a
playarea element in the
index.html. We are
going to create a log function that renders functions like
Yome.drawWalls into the
playarea element of our application page.
Above, I have created a utility function
playArea that allows us
to see the visual output of function that returns React SVG elements.
You can paste in the following commented out
drawWalls expressions to
help verify how our
drawWalls function is working.
If you uncomment the first line you should see the following in your browser:
You can also uncomment the other examples and see that we are indeed successfully drawing the regular polygons that we are looking for.
Now, leave one of the logging functions uncommented, so that it renders each time you save the file.
You can now modify the
Yome.sidePoints function to change the radius
of the points in the polygon. Modify the radius which is
180 in the
Yome.sidePoints function. Go ahead and try
values of 50, 120 and then back to 180.
As you can see, we now have a way to quickly examine the output of our DOM emitting functions live. This is very helpful.
Comment out the
playArea logging functions above before you go on.
Windows and Doors
Let’s draw some windows and doors.
I’m planning on abusing the ability of SVG to rotate things. So, I am going to just worry about drawing an item in one position and then letting SVG rotate it to the appropriate side of the Yome.
For all of the different things that are being drawn I just need to find the set of radial points that will outline the needed item.
Let’s start with windows.
First we create a function
windowPoints that creates the points for
our window. Using radial points, thinking about this is pretty
straightforward. We tighten the radius in from the walls a little. We
then create points that are close but indented from the corners of the
side polygon and then we connect these to a point that has an even
shorter radius and is centered on the side of the Yome.
If you uncomment the second
playArea call you will see this:
Great, we have a window!! Now you can, of course, play with the
dimensions of this window by adjusting the
I’m going to follow this same process for the rest of the drawn items, starting with doors.
The big difference with the door and the rest of the items is that they are drawn at the corner, whereas the window is drawn at a face.
If you render a door and a window you will see this:
The zip door is a little challenging because it isn’t just a group of points that we turn into a polygon. A zip door is a set of lines.
Here is a zip door:
Then there is the stove vent which is an ellipse.
And here is a stove vent:
Well this is great. We have quickly built up our drawing primitives and they are all functional.
We may want to reflect at this point: Why is it so effortless to display and work with our different drawing functions?
You may have noticed a familiar pattern in the drawing functions
above. Those of you who had an OOP education may see the old
shape polymorphism exercise: the classic example where you have a
Drawable class or interface that defines a
draw method. Then you
can pass a list of drawable things to a renderer and the renderer will
draw them. Easy peasy.
But here is the deal: Why the heck would I want to do this? Yes I do want dispatch, but creating a bunch of objects that have different draw methods is a lot of ceremony when I just want to dispatch on some type of data. If I want dispatch, why not just create it directly?
Here is my dispatch:
Now I have a way of rendering an item based on its type items. Simple! This is also extensible:
Now in order to use this render function we are going to need some data. Let’s create a more interesting Yome by adding some features to the sides.
If you print out the resulting
exampleData you will see that each
side has an optional
face can have a value of
corner can be “door-frame”, “zip-door” or “stove-vent”.
Let’s put our method dispatch to work to draw a complete yome along with its features.
First, we need a way to draw a single slice (side) which is a grouping of a corner and a side.
sideSlice function returns a rendering of a face and a corner of
one of the sides of the Yome layout. It also uses SVG functionality to
rotate it into its appropriate position in the drawing.
If you uncomment the first log statement you will see the 6th side of the eight sided Yome, which has a door and a window, rendered in its correct position like this:
So this is cool, we have a working dispatch
itemRender function and
we are successfully able to render a side of the example Yome. There
is very little we have left to do to render the whole Yome
drawYome function above, we just draw the walls of the Yome
and then draw all the features. You’ll notice that I also rotate the
drawing half a slice. This is because I like having the flat sides in
alignment with the rectilinear universe of the webpage.
Now that we can render our Yome, let’s add some functions to
render our widget to the
app element in our
At this point, we have created all the code needed to draw our Yome widget.
Straightforward? Am I right?
Do we need anything more than the above to express how to draw our Yome layout clearly?
This is straightforward, right? The code above is both understandable and reduces cognitive overhead.
Not only that, we can also reload it live and verify the output of the individual functions with great ease.
Since it’s possible to express things this way, this begs an important question: Why would we create complexity when it isn’t needed?
Let’s add a way to control the number of sides in our Yome layout.
We are now in a place where we are going to have to change the state of the program. An input event is going to get fired from the user and we are going to have to:
- change the program state
- rerender the Yome
These side effects are absolutely unavoidable in an interactive system. Because there is no way to get by without them, I’m going to call them the essential complexity of the program.
There are innumerable ways to express how the state is changed and the widget is re-rendered. But since these side-effects are unavoidable I’m going to let the needs of the actual program dictate how this should be done. Again, I’m keeping to my reductive stance of not adding anything that isn’t needed.
Let’s look at this event handler function. All the events in our system
are going to be composed of this
eventHandler. This function takes a
f that will be changing the state of the
application in response to an event. This
builds and returns an event handler that will call this side-effecting
f and then re-render the widget after the state transition.
Yep, it is that simple. I have handled the requirements for both state transition and re-rendering.
Normally, we would have some sort of state object that fires an event
when it changes. We would then hook the re-render of the widget to that
change event. But this really is not needed for this small application.
Instead, I am just sneaking a call to
Yome.render() into the
eventHandler so that it occurs after a state change.
Keep in mind that this is the first part of this program that I will change as our event listeners start having more sophisticated needs (like asynchronous Ajax calls). Again, the added complexity is just not needed here.
Let’s look at how this
eventHandler is used:
changeSideCount method does just what its name states. It
changes the number of sides on the Yome. It does this by directly
changing the global state. This is the essential complexity of
the program and we are going to have to do it one way or another, so
why not the easiest most straightforward way?
So far, we have minimized the side effects of this entire program to
eventHandler and the
Let’s use these functions to change the Yome layout directly from our widget.
Here is a select input that allows you to change the Yome size. If there is a change it fires our event handler which in turn will change the state of the application and re-render it.
It’s interesting to note, the
sideCountInput function is still a pure
function that returns some virtual DOM. This virtual DOM in turn
references another function
changeSideCount that will change the
state. The function itself is still a pure function.
If you uncomment the
React.render call, you can see our new size
control and if you select the different sizes you should see the Yome
Let’s integrate this
sideCountInput into the widget proper. Go edit
Yome.widget function so that it now looks like this:
Now our size control is part of the widget:
The window controls
Now it’s time to build the controls that let us add and remove windows from the Yome layout.
This code follows the pattern that we just established with the size
control. We create a bunch of absolutely positioned window control
links around the perimeter of the Yome layout. When someone clicks on
a link we change the state by taking the chosen side and adding the
"window" to the
face property or nulling it out.
We’ll add the window controls to our
Now the widget should render like this:
And the Yome widget comes to life!
Straightforward code as an enabler
The full Yome widget is not a trivial application, but by using a straightforward approach I was able to create something that works for my friend in a relatively short period of time and managed to have fun while doing it.
This is important. The straightforwardness of the code enabled me to just continue to do the next task at hand and in the end provide actual value to my friend.
Another way to say this: by addressing the complexity directly at the level of computation I was able to write this program correctly with less overall effort.
Problems that aren’t
The programming pattern used in this widget does have some potential problems that will show themselves as an application built like this grows. These are not problems for the program as presented but it’s important to mention some of the looming gotchas.
Asynchronous Event Handlers
It’s very likely that we are going to need asynchronous code in our event handlers at some point.
eventHandler can’t handle asynchronous callbacks and
if asynchronous code is used, it’s possible that
Yome.render will be
called before the state has actually changed. As soon as this type of
behavior is needed it’s better to move to a pattern where the render
happens explicitly as a result of a state change.
This code currently doesn’t lend itself to being used more than once in a client environment.
This again isn’t a problem for the current use case, but can be easily remedied by enclosing it in its own React component and storing the state in the local state of the component. This doesn’t require much refactoring at all. This also solves the previous asynchronous handler problem, because the component automatically re-renders when the local state of a React element changes.
You could also componentize the program without using a React wrapper and there are many well known patterns to do this.
Here is a quick re-factor to make this widget modular.
Unsafe data structures
The biggest problem that I have with the above code is that someone may accidentally modify the state during the render phase.
Yome.state is mutable and any function that has a reference to
it during the render phase may accidentally change the state while it
is, say, conjuring up some derivative state for some other function.
This is easier to do than one may think.
This could definitely cause some hard to find bugs and wreak havoc on the apparent independence of the functions that we are creating. It is for this reason that I would very likely start using immutable (persistent) data types like the ones found in Immutable-js. Immutable data is the most efficient way to prevent functions from inadvertently side effecting and accidentally ruining the data for the application. Immutable data guarantees that our pure functions are indeed independent.
Refactoring the Yome widget to use immutable data would be easy. But again, for this widget, it really isn’t needed yet. The data transitions are few and it’s easy to keep track of the few code sections that do mutate data.
Adding Immutable.js to the Yome widget addresses all the problems outlined above and would be a fantastic exercise. Have a look at the Immutable.js cursor and see if that is helpful in making the code more modular.
If folks are interested, I will do a follow-up post that addresses these things.
If we could get away with doing pure functional computation all the time, life would be awesome. However, we will eventually run into computational limits.
When using pure functions to create a complete Virtual DOM representation of an application view, it is possible that the size of our Virtual DOM tree will get too bulky for the React differencing algorithm to complete quickly. It is at this point that I will start breaking the application down into some well-placed React components to trim the Virtual DOM tree a bit.
However, there is a limit, and when you reach it there will probably be an obvious bottleneck, some large section of the DOM that doesn’t change that often.
You can take care of these bottlenecks fairly simply if you are using
Immutable.js to hold your application data. The following
memoizeReact function can make extremely short work of trimming the
Virtual DOM tree.
You can use
memoizeReact to create memoized React
functions like this:
drawWindow function will only be executed when the
drawWindowMem have changed value.
This simple memoize pattern can extend this purely functional approach quite far.
Thanks for reading this and I hope you found something useful here.
Special thanks to William Blasko(@wukkuan), Egg Syntax(@eggsyntax), Sean Omlor(@seanomlor), James Long(@jlongster) and Dominique Ferraton for reviewing this post.
- Programming with Hand Tools
- Why Why Functional Programming Matters Matters
- Out of the Tar Pit
- Local state is Poison
I have created a gist for comments: