Pure functions are easier to read and understand. All the function’s dependencies are in its definition and are therefore easier to see. Pure functions also tend to be small and do one thing. They don’t use this, a constant source of confusion.
Chaining
Chaining is a technique used to simplify code where multiple methods are applied to an object one after another.
Now let’s look and compare the two styles: imperative and functional. In the functional style, I’ll use the basic toolbox for list operations filter() and map() by chaining them together.
//Imperative style var filteredTasks = []; var task, i; for(i=0; i<tasks.length; i++){ task = tasks[i]; if (task.type === "RE") { filteredTasks.push({ id : task.id, desc : task.desc }); } }
//Functional style function isReviewTask(task){ return task.type === "RE"; }
function toTaskViewModel(task) { return { id : task.id, desc : task.desc }; }
var filteredTasks = tasks.filter(isReviewTask) .map(toTaskViewModel);
Notice the callbacks for filter() and map() as pure functions with intention revealing names.
Partial Application
Next I want to look into how we can improve readability and also reuse an existing function. Before doing that, we need a new function in our toolbox.
Partial application refers to the process of fixing a number of arguments to a function.
It’s a way to go from generalization to specialization.
We can create the partialRight() function and make it available to all functions by adding it to the Function.prototype . Or we can use one from a library like underscore.js or lodash.js.
Let’s say we want to refactor the flowing code to a functional style:
var priorityTasks= []; var task, i; for(i=0; i<tasks.length; i++){ task = tasks[i]; if (task.type === "RE" || task.type === "NC") { priorityTasks.push(task); } }
As I said, this time we want to create a generic function that can be used for filtering by any task type. Below we can see isTaskOfType() as the generic function, and how partialRight() can be used to create new predicates that will filter by specific types.
A predicate function is a function that takes one item as input and returns true/false based on whether the item satisfies a condition.
var isTaskOfType = function(task, type){ return task.type === type; }
var isNewContent = isTaskOfType.partialRight("NC"); var isReview = isTaskOfType.partialRight("RE");
Next I’ll use the two new predicates. Notice the filter’s callback. It has a name expressing its intention. It’s code may change and get more complex, but when I’m reading tasks.filter(isAPriorityTask) I clearly understand what kinds of tasks I’m selecting.
function isAPriorityTask(task){ return isNewContent(task) || isReview(task); }
var priorityTasks = tasks.filter(isAPriorityTask);
Reduce
I’ll start a new example using a shopping list, and I’ll compute the total price and also the price for fruit only. Below is the imperative style:
var totalPrice = 0, fruitsPrice = 0, i, line; for(i=0; i<shopingList.length; i++){ line = shopingList[i]; totalPrice += line.units * line.price; if (line.type === "fruits") { fruitsPrice += line.units * line.price; } }
Taking the functional approach in this case will require the use of reduce() to compute the total price.
The reduce() function is used to accumulate all values of the collection into one value.
As we did before, we’ll create new functions for the required callbacks and give them intention reveling names : computePrice() and areFruits().
function computePrice(totalPrice, line){ totalPrice += line.units * line.price; return totalPrice; }
function areFruits(line){ return line.type === "fruits"; }
var totalPrice = shopingList.reduce(computePrice, 0); var fruitsPrice = shopingList.filter(areFruits) .reduce(computePrice, 0);
Conclusion
Applying Functional Programming to list operations will break the operations in steps like: filter, map, reduce, and sort. At the same time, it will require you to define new pure small functions to support those operations.
By combining Functional Programming with the practice of giving Intention Reveling Names to these new functions, we can greatly improve the readability of the code.