This article discusses functional programming in JavaScript through examples (jQuery, AngularJS, Underscore, JSCheck, Lazy.js, React, Immutable.js, ect.).

Please Sign up or sign in to vote.

Functional Programming In JavaScript By Example

Table Of Contents

Introduction

Functional programming is a programming style based on two principles:

First-class functions: A function can be named, affected and typed. A function can be defined and created on demand. A function can be passed as the argument of another function. A function can be returned from another function. A function can be stored in a data structure. Purity of functions: Reject side effects and state. Advocate immutability of data structures.

This article will discuss functional programming in JavaScript through examples (jQuery, AngularJS, Underscore, JSCheck, Lazy.js, React, Immutable.js, ect.).

Functions In JavaScript

A function in JavaScript can be defined in the following way:

function (param1, param2, ...){ };

Or in ECMAScript 6, in the following way:

(param1, param2, ...) => { };

Below an example:

let f = function (x, y){ return x + y; }; f( 4 , 5 );

An anonymous function can be defined anywhere in the code. And functions can be seen as code chunks, facilitating abstraction and reuse.

Closure In JavaScript

When a function is defined, the variables it uses that are not parameters are embarked by reference within the function as an immutable record.

For example, the Module pattern: Create an encapsulating structure that can store an internal private state, and

expose a public interface.

Below an example of the Module pattern:

let myModule = ( function (){ let conf = { useCaching: true , language: ' en' }; return { reportConfig: function (){ console .log( ' Caching ' + (conf.useCaching ? ' enabled' : ' disabled' )); }, updateCaching: function (caching){ conf.useCaching = caching; } } })();

The Module pattern enables a modular programming style.

First-Class Functions

To be a first-class function means that the function has the same state as a value such as an integer or a character:

A function can be named, affected and typed. A function can be defined and created on demand. A function can be passed as the argument of another function. A function can be returned from another function. A function can be stored in a data structure.

Some consequences:

If a function can be passed as argument to a function, it will imply the possiblity of generalization/functional abstraction. Every portion of code in a function can be replaced by an abstraction (function call). To be as a result of a function, allows: Adaptations of functions. Partial applications (currying). To be stored in a data structure allows: Dynamic modularity: Management of function sets, structured lists, tables, trees, graphs, etc. Passing arguments as function sets (instead of simple functions). Also, one of the consquences is data-driven programming.

Functions Taking Functions As Parameters

1. Callbacks in jQuery:

$.ajax ({ url: ' /api/getWeatherTemp' , data: { zipcode : 33333 }, success: function (data){ $( ' #weather−temp' ).html( ' <strong>' + data + ' </strong> degrees' ); } });

2. Generalized code via higher order functions is easier: each, map, reduce, filter, ect.

3. JsCheck is a specification-based testing tool based on the ideas of Quickcheck in Haskell, and developed by Crockford. Each function under test is associated to an abstract specification in the form of a set of predicates.

For example:

Function under test: passwordScore(password) -> score

Specification:

All passwords without special characters must have a negative score.

Below the implementation:

JSC.claim( ' Negative score for passwords w/o special characters' , function (verdict, password, maxScore){ return verdict(passwordScore(password) < 0 ); }, [JSC.one_of([ JSC.string(JSC.integer( 5 , 20 ), JSC.character( ' a' , ' z' )), JSC.string(JSC.integer( 5 , 20 ), JSC.character( ' A' , ' Z' )) ]) ] );

Functions Returning Functions

Smoothen the use of higher order functions (cf. pluck in underscore.js):

function plucker(field){ return function (obj){ return (obj && obj[field]); }; }

Below an example:

let oldies = [ {name: ' pim' , color: ' green' }, {name: ' pam' , color: ' red' }, {name: ' pom' , color: ' blue' }]; _.map(oldies, plucker( ' name' ));

Also, another example is functions as chunks of code like in Underscore.js templates:

let compiled = _.template( " \ <% _.each(items, function(item, key, list){ %> \ <tr> \ <td> <%= key + 1 %> </td> \ <td> <%= item.name %> </td> \ </tr> \ <%}) %>" ); compiled({items: oldies});

Gives the following result:

< tr > < td > 1 < /td > < td > pim < /td > < /tr > < tr > < td > 2 < /td > < td > pam < /td > < /tr > < tr > < td > 3 < /td > < td > pom < /td > < /tr >

Function Composition

A natural way of manipulating functions via composition. Below some examples:

1. Write code in a declarative manner - Underscore.js chain:

_.chain([ 1 , 2 , 3 , 200 ]) .filter( function (num){ return num % 2 == 0 ; }) .tap(alert) .map( function (num){ return num ∗ num; }) .value();

Here composition appears as the composition of methods via the "." operator.

2. Compose abstract creation rules to create complex objects – AngularJS routes:

$routeProvider .when( ' /' , { controller: ' ProjectListController as projectList' , templateUrl: ' list.html' , resolve:{ projects: function (projects){ return projects.fetch() }}}) .when( ' /edit/:projectId' , { controller: ' EditProjectController as editProject' , templateUrl: ' detail.html' }) .otherwise({ redirectTo: ' /' });

Functions can be decorated, and code can be added before, after and around the call of the function.

For example: Underscore.js wrap function.

User.prototype.basicLogin = function (){ }; User.prototype.adminLogin = _.wrap(User.prototype.basicLogin, function (loginFun){ let hasAdminPrivs = this .admin; if (!hasAdminPrivs) console .log( ' !! Cannot login ' + this .name + ' as admin' ); else { loginFun.call( this ); console .log( this .name + ' has logged in as admin' ); } } );

Currying Or Partial Application

Currying is a process of transforming a function of multiple arguments into a sequence of functions each with a single argument.

For example:

function plus_plain(x, y){ return x + y ; } plus_plain( 4 , 5 );

Can be written as follows:

function plus_curry(x){ return function (y){ return x + y; } } plus_curry( 4 )( 5 );

The advantage is that in the curried form, there is a possibility to partially apply a function.

Below an example with the partial function of Underscore.js:

let sendAjax = function (url, data, options) { }; let sendPost = _.partial(sendAjax, _ , _, {type: ' POST' , contentType: ' application/json' });

This allows to specialize generalized functions.

Currying directly leads to the lazy evaluation mechanism.

Data Structures

Some data types compose well with functional programming. For example, jQuery selectors mechanism is a way to represent set of DOM nodes:

$( ' li' ).filter( ' :even' ).css( ' background−color' , ' red' );

A DOM tree can be manipulated via higher-order functions (cf. Crockford):

function walk_tree(node, fun){ fun(node); let tmpnode = node.firstChild; while (tmpnode){ walk(tmpnode, fun); tmpnode = tmpnode.nextSibling; } }

And its functional version:

function fold_tree(tree, fun, start){ let newstart = fun(start, tree); return _.reduce(tree.children, function (cur, subtree){ return fold_tree(subtree, fun, cur); }, newstart); }

Data-Driven Programming

Data-driven programming is a programming technique where the data itself controls the flow of the program and not the program logic. In functional programming, the data may be a set of functions.

For example:

let funs = [ { name: ' cool' , deps : [] , func: function (){ console .log( ' cool' )}}, { name: ' hot' , deps : [] , func: function (){ console .log( ' hot' )}}, { name: ' temp' , deps : [ ' cool' , ' hot' ], func : function (cool ,hot ,val){(val > 10 ) ? hot(): cool() }}]; let injector = function (key){ let fobj = _.find(funs, _.matcher({ name : key })); let fdeps = _.map(fobj.deps, injector); return function (){ let fullargs = fdeps.concat(arguments); fobj.func.apply(fobj, fullargs); } } injector( ' temp' )( 12 ); injector( ' temp' )( 8 );

Connections between functions is handled by injectors.

Control Of Execution

About the ordering of computations in the code, several strategies are available:

Call by value: evaluate every function call at the point of definition,

Call by need: leave the functions unevaluated until needed.

For example, Lazy programming - Lazy.js:

let lazySequence = Lazy(array) .filter(_.matcher({ category : ' cat' })) .take( 20 ) .map(template);

The evaluation is delayed until needed. Thus there are no intermediary arrays. This also allows efficient operations on (possibly infinite) streams.

Another example is Asynchronous Module Definitions - require.js: Control module dependencies to ascertain their loading in the correct order.

Below an example:

define([ ' dep1' , ' dep2' ], function (dep1, dep2){ return function () { }; });

Memoization

A function is referentially transparent if given the same parameters, it will always return the same results.

Memorization is an interesting feature offered by the usage of pure functions.

Caching results, a simple form of lazy programming - Underscore.js (memoize):

Function.prototype.memorize = function (){ let self = this , cache = {}; return function (arg){ if (arg in cache){ console .log( ' Cache hit for ' + arg); return cache[arg]; } else { console .log( ' Cache miss for ' + arg); return cache[arg] = self(arg); } } }

Memoization can be done automatically

Purity

A pure function in programming is a function in the mathematical sense, i.e. there is only one possible result for each possible arguments, and no other effect:

Independence from context (do not read from external state),

Referential transparency (invariant behavior),

No side-effect (do not write to external state).

Some consequences:

Simpler unit testing,

Easier parallelization of code (think map/reduce),

Easier static checking.

Immutable Structures

Immutable-JS:

Provides several immutable data structure: List, Stack, Map, Set, ect.

Maximises sharing and takes advantage and laziness for efficiency.

For example, PureRenderMixin in React.js using Immutable-JS structures:

createElem( " div" , createElem( " center" , createElem( " a" )), createElem( " table" , createElem( " tr" ) , createElem( " tr" ) , createElem( " tr" )) );

When rendering, the render function is called only if the element has changed.

Caveats

Lack of complete static checking hinders functional programming.

Pure JavaScript tools have a limited scope: Crockford JSLint: Avoid anonymous functions within a loop. Google Closure compiler: calling a non-function variable, wrong arguments count.

Tendency to evolve towards compilers to Javascript: Facebook Flow, Microsoft TypeScript, LLVM Emscripten, ect.



Among the missing features:

Static type checking, with genericity for higher-order functions.

Static verification at the modular level (sets of functions or interfaces).

History