Thinking about data functionally

I've mentioned previously that D3 is very functionally designed, meaning that it uses some of the idioms JavaScript has adopted from functional programming. Although we can still approach D3 development in a very classical, object-oriented fashion, our lives will be much easier if we start thinking about our code and data with a functional mindset.

The good news is that JavaScript almost counts as a functional language; there are enough features to get the benefits of a functional style, and it also provides enough freedom to do things imperatively or in an object-oriented way. The bad news is that, unlike real functional languages, the environment gives no guarantee about our code.

Later on, in Chapter 9, Having Confidence in Your Visualizations, we'll look at TypeScript, which allows compilation of JavaScript using static types, and Tern.JS , which analyzes code in order to improve tooling. These efforts go a great deal toward improving confidence in how data moves through our visualizations, in addition to improving our tooling.

In this section, we'll go through the basics of functional-style coding and look at wrangling the data so that it's easier to work with. If you want to try proper functional programming, I suggest that you refer to Haskell in Learn You a Haskell for Great Good, free to read and available at http://learnyouahaskell.com/.

The idea behind functional programming is simple: compute by relying only on function arguments. It's simple, but its consequences are far reaching.

The biggest consequence is that we don't have to rely on state, which in turn gives us a referential transparency. This means that functions executed with the same parameters will always give the same results, regardless of when or how they're called.

In practice, this means that we design the code and dataflow, that is, get data as an input, execute a sequence of functions that pass changed data down the chain, and eventually get a result.

It also emphasizes immutability, whereby functions are written to prevent side-effects and changes to the underlying data as it passes through your code. In Chapter 2, A Primer on DOM, SVG, and CSS, we used Array.from() to make a copy of an argument so that our function wouldn't mutate the data passed to it. We also generally assign variables using the const keyword, so we know to make a copy, and will get errors if we try to reassign a variable. We'll continue to do this to some extent though we mainly make use of ES6's concept of immutability, insomuch that it only protects against reassignment using the const keyword, we still have the ability to extend objects and interact with arrays using methods, such as Array.prototype.pop() and Array.prototype.unshift(), as per usual.

In the following examples, I give the full names of the native prototype methods so as to differentiate them from d3-selection's filter and map methods. Array.prototype.map, thus, refers to the map method on the Array primitive's prototype object.

What's a prototype, though? JavaScript is a prototype-based language, meaning that everything is effectively an object that inherits from another object, its prototype. All arrays are descendants of the Array object, thus, they inherit its prototype methods, such as .map(), .reduce() and .filter(). The Array prototype itself inherits other prototypes, all the way up the chain to Object.prototype. D3 selections are also descendants of the Array prototype, but D3 then goes on to replace some functions, such as .filter() and .sort() with its own versions adapted for selections, in addition to adding a few other helpful methods, such as .each, or to come full circle with the whole naming thing, d3.selection.prototype.each (or simply selection.each as it's referred to in the documentation). Even ES2015 classes, which use a much different inheritance model and come from object-oriented programming, are ultimately still prototype-based.

You've already seen this functional approach in previous examples, particularly in Chapter 2, A Primer on DOM, SVG, and CSS. Our dataset started and ended as an array of values. We performed some actions for each item, and we relied only on the current item when deciding what to do. We also had the current index so that we could cheat a little with an imperative approach by looking ahead and behind in the stream.

Although I endeavor to take a somewhat functional approach throughout the rest of the book, there's no way I could adequately explain the nuances of what is a really interesting and important practice within modern JavaScript development. For a good series on the topic, I suggest Eric Elliott's excellent series that can be found at https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset