Applicative Programming In JavaScript With lodash.js

In general, applicative programming is the pattern of defining a function that takes a function and then invokes that function for each element in a collection/list. To apply this definition of applicative programming to JavaScript, you will need to know a little something about first-class functions before we dive in. So, let's start there.

JavaScript has first class functions. This means that instances of the Function constructor can go and do anything any value in the language can do because functions in JavaScript are values.

For example, in the code example below I demonstrate that the value 5 can be saved to a variable, passed to a function, and returned from a function.

Now, because JavaScript functions are values (i.e. first-class citizens), it is possible to replace the value 5 in the previous code example with the value function(){}.

Because we can replace 5 with function(){} without an error, it is clear that JavaScript functions can be assigned to variables, passed as arguments to other functions, and returned from functions just like numeric values. In fact, any JavaScript value could be used in place of 5 but it just so happens that today we care about the fact the functions share in a first-class citizenship. If it helps, think of functions like any other value passed around in JavaScript, except that this value can be invoked using ().

Another way to think about first-class functions is to consider what they allow you to accomplish when programming. It is said that a programing language has first-class functions if it can do the following:

1. Create new functions from preexisting functions at run-time (aka Partial application)

2. Store functions in collections (i.e. Objects and Arrays)

3. Use functions as arguments to other functions (aka higher order function)

4. Use functions as return values of other functions (aka higher order function)

The last four code examples make it crystal clear that JavaScript has first-class functions. Due to this capability, JavaScript, while not strictly a functional programming language, can facilitate many of the programming paradigms found in functional languages. For example, JavaScript natively makes use of applicative programming paradigms (an aspect of functional programming) by providing the native array and string methods list below, all of which accept function values to be applied to each element/character of an array or string.

Examine each of these native methods below and take notice that each either expects, or optional takes, a function that it invokes on its elements.

[].forEach() - Executes a provided function once per array element

[].every() - Tests whether all elements in the array pass the test implemented by the provided function

[].filter() - Creates a new array with all elements that pass the test implemented by the provided function

[].map() - Creates a new array with the results of calling a provided function on every element in this array.

[].some() - Tests whether some element in the array passes the test implemented by the provided function

[].reduce() - Apply a function simultaneously against two values of an array (from left-to-right) as to reduce it to a single value

[].reduceRight() - Apply a function simultaneously against two values of an array (from right-to-left) as to reduce it to a single value

[].sort() - Optionally accepts a function that defines the sort order

"".replace() - Optionally takes a function which passes the matches to the function

For many, myself included, what is provided natively is neat but it does not exhaust what is possible in terms of applicative programming or satisfy ones desire to program in a functional style. This is why it is common to see JavaScript programmers using these functions, not just on arrays or strings as intended, but really on any type of object that has numeric property names and a length property. For example, when a primitive string such as 'blue' is acted upon by a modern JavaScript interrupter, an object wrapper is created so that string methods (e.g. 'blue'.length === 4) can be called. Essentially, 'blue' is converted to {0:b,1:l,2:u,3:e,length:3}. Wouldn't it be great if we could use the forEach() method on 'blue'? Well, you can, thanks to first-class functions and the fact that many of the array methods can be generally applied to objects.

In the code below I am invoking the Array forEach() function as if it was a method on {0:b,1:l,2:u,3:e,length:3}. This is done by invoking forEach() using call() and passing the this value for the forEach() method (i.e. the value of this when forEach() is invoked) as well as the function argument expected by the forEach().

Another common example seen in the wild, which re-purposes forEach(), is the iteration of the arguments array-like object.

Iterating over a string (really an object with numeric property names and a length property) or array-like object by borrowing array methods certainly falls into the category of applicative programming, however it is a bit of a language hack and requires a binding ceremony. Not to mention doing this comes at a performance cost.

What is really needed is a utility that provides applicative-like programming for generic collections without using call(). Of course, more applicative sugar extending what can be done with Object objects or Array objects would be nice too.

There are several solutions for JavaScript developers that provide a set of functions that build upon the nature of the applicative array methods found in JavaScript. I've listed the common solutions below.

All of the above mentioned utilities/libraries provide functions for operating on generic collections. For instance, the each() (aka forEach()) function from most will agnostically operate on an Array, Object, or String object.

//all of these log f,o,o to the console

_(['f','o','o']).each(function(e){console.log(e)});

//note no .length property on this object, but it still works
_({0:'f',1:'o',2:'o'}).each(function(e){console.log(e)});

_('foo').each(function(e){console.log(e)});

Contrasting this to the native forEach(), which only operates on arrays unless we pull some tricks with call() and you can see the benefits of using a functional utility library that provides applicative programming functions.

The above each() method is only the tip of the iceberg, but I hope it is enough to get you excited about applicative programming with JavaScript and using a low level utility library that makes doing applicative programming easier and more powerful.

If you need more convincing, consider that many of these utilities additionally provide functions/utilities for:

I am not going to discuss these additional benefits of using a functional utility library in this article. I will save these parts for another article. What I want to conclude this article with is some discussion about lodash.js, my functional library of choice, and the methods it provides for doing applicative programming on collections, arrays, and objects. While lodash.js provides way more than applicative programming functions (i.e. consistency, performance, & features you can't get out of native), I'm hoping I can get you hooked on lodash.js by showcasing the improvements and additions lodash.js offers over the native applicative programming methods we examined at the start of this article.

As previously demonstrated, JavaScript out of the box provides 8 array methods (forEach(), every(), some(), filter(), map(), reduce(), reduceRight(), sort()) and one string method (i.e. replace()) for doing applicative programming. You should learn these and know them. Once you are comfortable with how these methods work, you should move on to learn the many additions and improvements lodash.js provides for operating on collection's (i.e. an array, object, or string), Object's, and Array's with function arguments.

The following three tables provide all of the methods that lodash.js offers for doing applicative programming. I would encourage you to spend time reading the descriptions and mentally storing these functions in your mind for future use in your programs. I have no doubt that you will benefit from knowing them and using them.

Collections (i.e. an array, object, or string):

Method (includes all aliases) **Callback** Arguments Description Returns
_.all, _.every (value, index|key, collection) Checks if the given callback returns truey value for all elements of a collection Boolean
_.some, _.any (value, index|key, collection) Checks if the callback returns a truey value for any element of a collection Boolean
_.map, _.collect (value, index|key, collection) Creates an array of values by running each element in the collection through the callback New Array
_.filter, _.select (value, index|key, collection) Iterates over elements of a collection, returning an array of all elements the callback returns truey for New Array
_.invoke   Invokes the method named by methodName on each element in the collection returning an array of the results of each invoked method New Array
_.reject (value, index|key, collection) The opposite of _.filter this method returns the elements of a collection that the callback does not return truey for New Array
_.sortBy (value, index|key, collection) Creates an array of elements, sorted in ascending order by the results of running each element in a collection through the callback New Array
_.countBy (value, index|key, collection) Creates an object composed of keys generated from the results of running each element of collection through the callback New Object object
_.groupBy (value, index|key, collection) Creates an object composed of keys generated from the results of running each element of a collection through the callback New Object object
_.indexBy (value, index|key, collection) Creates an object composed of keys generated from the results of running each element of the collection through the given callback New Object object
_.detect, _.findWhere, _.find (value, index|key, collection) Iterates over elements of a collection, returning the first element that the callback returns truey for Item/Element From Collection
_.findLast (value, index|key, collection) This method is like _.find except that it iterates over elements of a collection from right to left Item/Element From Collection
_.max (value, index|key, collection) Retrieves the maximum value of a collection. If the collection is empty or falsey -Infinity is returned. If a callback is provided it will be executed for each value in the collection to generate the criterion by which the value is ranked Item/Element From Collection
_.min (value, index|key, collection) Retrieves the minimum value of a collection. If the collection is empty or false, Infinity is returned. If a callback is provided it will be executed for each value in the collection to generate the criterion by which the value is ranked Item/Element From Collection
_.forEach, _.each (value, index|key, collection) Iterates over elements of a collection, executing the callback for each element Collection Passed In
_.forEachRight, _.eachRight (value, index|key, collection) This method is like _.forEach except that it iterates over elements of a collection from right to left Collection Passed In
_.reduceRight, _.foldr  (accumulator, value, index|key, collection) This method is like _.reduce except that it iterates over elements of a collection from right to left The Accumulated Value
_.reduce, _.foldl, _.inject  (accumulator, value, index|key, collection) Reduces a collection to a value which is the accumulated result of running each element in the collection through the callback, where each successive callback execution consumes the return value of the previous execution The Accumulated Value

Objects:

Method (includes all aliases) **Callback** Arguments Description Returns
_.clone (value) Creates a clone of value. If deep is true nested objects will also be cloned, otherwise they will be assigned by reference. If a callback is provided it will be executed to produce the cloned values Cloned Object
_.cloneDeep (value) Creates a deep clone of value. If a callback is provided it will be executed to produce the cloned values. If the callback returns undefined cloning will be handled by the method instead Cloned Object
_.findKey   This method is like _.findIndex except that it returns the key of the first element that passes the callback check, instead of the element itself String
_.findLastKey   This method is like _.findKey except that it iterates over elements of a collection in the opposite order String
_.assign, _.extend  (objectValue, sourceValue) Assigns own enumerable properties of source object(s) to the destination object. Subsequent sources will overwrite property assignments of previous sources. If a callback is provided it will be executed to produce the assigned values Object
_.forIn (value, key, object) Iterates over own and inherited enumerable properties of an object, executing the callback for each property. Object
_.forInRight (value, key, object) This method is like _.forIn except that it iterates over elements of a collection in the opposite order Object
_.forOwn (value, key, object) Iterates over own enumerable properties of an object, executing the callback for each property Object
_.forOwnRight (value, key, object) This method is like _.forOwn except that it iterates over elements of a collection in the opposite order Object
_.merge (objectValue, sourceValue) Recursively merges own enumerable properties of the source object(s), that don't resolve to undefined into the destination object. Subsequent sources will overwrite property assignments of previous sources. If a callback is provided it will be executed to produce the merged values of the destination and source properties. If the callback returns undefined merging will be handled by the method instead Object
_.omit (value, key, object) Creates a shallow clone of object excluding the specified properties. Property names may be specified as individual arguments or as arrays of property names. If a callback is provided it will be executed for each property of objectomitting the properties the callback returns truey for Object
_.pick (value, key, object) Creates a shallow clone of object composed of the specified properties. Property names may be specified as individual arguments or as arrays of property names. If a callback is provided it will be executed for each property ofobject picking the properties the callback returns truey for Object
_.transform (accumulator, value, key, object) An alternative to _.reduce this method transforms object to a new accumulator object which is the result of running each of its elements through a callback, with each callback execution potentially mutating the accumulator object Returns the accumulated value
_.isEqual (1st arg, 2nd arg) Performs a deep comparison between two values to determine if they are equivalent to each other. If a callback is provided it will be executed to compare values. If the callback returns undefined comparisons will be handled by the method instead Boolean

Arrays:

Method (includes all aliases) **Callback** Arguments Description Returns
_.remove (value, index, array) Removes all elements from an array that the callback returns truey for and returns an array of removed elements New Array
_rest, _.drop, _.tail  (value, index, array) The opposite of _.initial this method gets all but the first element or first n elements of an array. If a callback function is provided elements at the beginning of the array are excluded from the result as long as the callback returns truey Array
_.flatten (value, index, array) Flattens a nested array (the nesting can be to any depth). If isShallow is truey, the array will only be flattened a single level. If a callback is provided each element of the array is passed through the callback before flattening Array
_.initial (value, index, array) Gets all but the last element or last n elements of an array. If a callback is provided elements at the end of the array are excluded from the result as long as the callback returns truey Array
_.rest, _.drop, _.tail (value, index, array) The opposite of _.initial this method gets all but the first element or first n elements of an array. If a callback function is provided elements at the beginning of the array are excluded from the result as long as the callback returns truey Array
_.uniq, _.unique (value, index, array) Creates a duplicate-value-free version of an array using strict equality for comparisons, i.e. ===. If the array is sorted, providing true for isSorted will use a faster algorithm. If a callback is provided each element of array is passed through the callback before uniqueness is computed Array
_.sortedIndex (value) Uses a binary search to determine the smallest index at which a value should be inserted into a given sorted array in order to maintain the sort order of the array. If a callback is provided it will be executed for value and each element ofarray to compute their sort ranking Number - Returns the index at which value should be inserted into array
_.first, _.head, _.take (value, index, array) Gets the first element or first n elements of an array. If a callback is provided elements at the beginning of the array are returned as long as the callback returns truey Array or first element
_.last (value, index, array) Gets the last element or last n elements of an array. If a callback is provided elements at the end of the array are returned as long as the callback returns truey Array or first element
_.findIndex (value, index, array) This method is like _.find except that it returns the index of the first element that passes the callback check, instead of the element itself Number else -1
_.findLastIndex (value, index, array) This method is like _.findIndex except that it iterates over elements of a collection from right to left. Number else -1