Underscore Compose - Functional JavaScript

TL;DR:

Underscore is a great JavaScript utility to get introduced to functional programming. It’s built with a functional programming mindset(immutability and statelessness) while also promoting the use of Functions as 1st class citizens.

The underscore compose function allows the creation of a single function that acts as a wrapper to multiple chained function events. Using a composed function is a clean way to code as it moves modifications to the composed function to a singel location rather than needing to change duplicated code in multiple locations.

Functional Programming

Functional programming is about mapping a set of value inputs to a result set of value outputs. The key points of functional programming are

  1. Immutability - An object once created cannot be modified. The function does not modify that object as part of its role.
  2. Statelessness - A function is only infuenced by its current inputs. It cannot use variables that were not directly passed into it.
  3. Functions as first class citizens - Functions can be passed as a parameter and returned as an output.

Underscore

Underscore follows the functional paradigm. Take _.map as an example below. A new array of values based on a transformation of the each value in the in put array.

var a = ['b','d','m']
var result = _.map(a, function(element){
    return element + 'ad';
})
#=> ['bad', 'dad', 'mad']

Notice that there is not a dependency on any input outside of what was supplied to the function. This example demonstrates what was previously stated about functional programming:

  • Immutability - The input parameter is not changed by the application
  • Statelessness - The input parameter is the only driver for the output
  • Functions as first class citizens - The transformation function is an input to drive the resultant output

Underscore: Compose

Compose is great function that may not be used often, but provides an easy way to reduce chained functions to a single easily managed-in-one-location function that can be used across the application without duplicating the implementation code.

Below is the underscore implementation

function() {
    var args = arguments;
    var start = args.length - 1;
      var i = start;
      var result = args[start].apply(this, argum
    return function() {ents);
      while (i--) result = args[i].call(this, result);
      return result;
    };
  };

Notice that the first apply takes any number of arguments. Then a loop is entered to apply the remaining arguments (functions) where the input is the result from the previous function.

Example

Now an example implementation

var add = function(a,b){
    return a + b;
}
var rateconversion = function(value){
    return value * 1.5;
}
var composed = _.compose(rateconversion, add);

composed(6,4);
#=> 15

Great: a write once and repeat function. It’s almost guaranteed that as soon as this is released a user will need to use a different rate conversion other than 1.5. What can be done about that?

Pipeline Transformations

Why not pass an object literal through the pipeline where it can contains values the data needed for each chained transformation within the pipeline. Then continue to pass an object literal through the chain until the final solution is returned on the other side.

Below is the previous example using an object literal

var data = {
    a: 1,
    b: 2,
    rate: 1.5
}
var add = function(data) {
    data = _.clone(data);
    data.c = data.a + data.b;
    return data;
};
var convert = function(data) {
    data = _.clone(data);
    data.result = data.c * data.rate;
    return data;
};
var rateconversion = _.compose(convert, add);

rateconversion(data);
#=>{ a: 1, b: 2, rate: 1.5, c: 3, result: 4.5 };

#data remains unchanged
#=>{ a: 1, b: 2, rate: 1.5 }

Notice that the result is returned in data.result (4.5)

As an alternative the result could be return directly rather than wrapped in an object literal.

In the above example there is strict adherence to the immutability paradigm. The first step in the add and convert functions is to clone the input as later additional values are added to the clone input object and passed as the result.

The above example also demonstrates separate data from the implementation as the as rateconversion is used across an application different rates can be applied.

#Wrapping It Up See the TL;DR; section :-)

#Links