Closures, Partial Application and Currying: Effective Functional JavaScript

6,188 reads

To use JavaScript to its full potential you have to embrace its strong functional programming base. We’re going to explore some crucial and powerful functional constructs: closures, partial application and currying that make JavaScript terse yet understandable.

The basics

Functional programming is a programming paradigm that follows a more mathematical computation model. Let’s go through some basics to make your JavaScript more functional.

Declarative programming

Functional programs tend to be declarative (as opposed to imperative), that’s a case of telling the compiler what you want instead of how you want it.

const list = [ 1, 2, 3 ];

// imperative style

const imperative = list[0];

// declarative style

const declarative = firstElement(list);

In the above code snippet we’re trying to get the first element of list , the declarative example says what we want, firstElement can do whatever it likes as long as it returns the first element of the passed parameter. Whereas in the imperative style, we say I want index 0 of list explicitly. In JavaScript and at this program size, this doesn’t make a massive difference.

To build functional programs, we should prefer the declarative style and avoid mutation.

Recursion and higher order functions

There are no loops in functional programming, just recursion and higher order functions.

Mechanisms such as pattern matching allow for easier recursive function declarations. In ECMAScript 6 (the 2015 edition of the standard JavaScript is based on) we’ve added destructuring to the toolbox, which is a basic pattern matching that works for lists. You can read more about it here.

Higher order functions allow you to traverse iterable collections (Arrays). In JavaScript we have Array#map, Array#filter and Array#reduce. Each of these takes a function as an argument. This is possible because we have first-class functions in JavaScript, which means you can pass them around like any other type of variable :).

Lambdas (anonymous functions)

In JavaScript we can declare lambas (anonymous functions), which is quite handy considering the API of a lot of libraries expects a function as a parameter. We can just declare the function inline. It might mean a bit of a indentation/bracketing problem but inlining until you can generalise or refactor is actually great.

[ 1, 2, 3 ].map(function(el) {

return el * 2;

});

Closures

Here are some >140 character explanations of closures, thanks to Mateusz Zatorski for asking and his esteemed followers for answering :).

We can use closures to put state inside an outer function while having access to that state in an inner function. That state is not global but still accessible to child functions.

function outerFunction() {

const someState = { count: 0 };

return {

increment: function() {

someState.count++;

},

getCount: function() {

return someState.count;

}

}

}

const counter = outerFunction();

counter.increment();

counter.increment();

counter.getCount(); // 2

counter.increment();

counter.increment();

counter.getCount(); // 4

someState; // ReferenceError: someState is not defined

The state ( someState )isn’t global since the last statement returns an error. It is however available to the functions it returned, because they can “see” someState , it’s in their lexical scope.

Function application

Function application is the first “hardcore” functional programming concept we’re going to introduce today. It’s a concept that comes from the mathematics world.

A function application, in JavaScript, can look like a function call but doesn’t have to be.

function someFunc(arg1, arg2) {

return arg1 + arg2;

}

// function call

someFunc(1, 2); // 3

// different ways to apply a function

someFunc(1, 2); // 3

someFunc.apply(this, [ 1, 2 ]); // 3

someFunc.call(this, 1, 2); // 3

A function call is an imperative construct whereas a function application belongs to the realm of functional programming and mathematics.

In JavaScript you can even use apply and call to define what this will be set to during the application.

Partial application

Partial application is when you apply some of the required parameters of a function and return a function that takes the rest of the parameters.

We’re going to flip the parameters to the map function. Instead of taking parameters (list, fn) it’s going to take (fn, list) . This is to illustrate the value of partial application.

function functionalMap(fn, list) {

return list.map(fn);

}

function partialFunctionalMap(fn) {

return function(list) {

return functionalMap(fn, list);

}

}

// Example 1

// Let's apply all the arguments at once

functionalMap(x => x * 2, [ 1, 2, 3 ]);

functionalMap(x => x * 2, [ 2, 3, 5 ]);

// Example 2

// Let's apply them one at a time

const doubleListItems = partialFunctionalMap(x => x * 2);

doubleListItems([ 1, 2, 3 ]);

doubleListItems([ 2, 3, 5 ]);

What the code does in example 1 is less obvious than in example 2. You have to read what the lamba does instead of being told by the variable function name.

This is something we can use in places like React event handlers.

Now if we went the Object-oriented route, we would use .bind partialHandleClick function to the component instance ( this ) and to be able to access this.props.activeType from inside partialHandleClick . We’re trying to leverage functional programming, so no accessing this from all the way inside an event handler. We get to store some information that we can get at .map time (which type is this handler for). When the event triggers, we get the final parameter we need e (the event object) and the handler can finish applying.

Currying

A curried function is a function that you apply 1 parameter at a time.

function partialFunctionalMap(fn) {

return function(list) {

return functionalMap(fn, list);

}

}

partialFunctionalMap is curried. In the event handler example partialHandleLinkClick isn’t, since the first application provided 2 parameters.

We could rewrite it though.

function curriedHandleLinkClick(type){

return function(activeType) {

return function(e) {

const hasKeyboardModifier = e.ctrlKey || e.shiftKey || e.altKey || e.metaKey;

updateType(type, activeType, hasKeyboardModifier);

};

};

}

And we would use this.curriedHandleLinkClick(type)(this.props.activeType) instead of this.partialHandleLinkClick(type, this.props.activeType) .

This isn’t as pretty in JavaScript as in other languages since we’re replacing (arg1, arg2, arg3) with (arg1)(arg2)(arg3) .

Currying and partial application

Currying is strict: a curried function always applied 1 parameter at a time. Partial application is not this strict.

A curried function tends to be partially applied but a partially applied function does not have to be curried.

This means we can automate the currying process.

In JavaScript we can use libraries to curry functions with multiple arguments. Lodash has a curry function and so does Ramda. They take a function and when applied with a parameter either returns if all required arguments are present or returns a curried function that accepts the rest of the arguments.

You can also write your own by accessing the arguments object of the function and using Function#apply. Here are a couple of tutorials that take you through this process.

Some languages like Haskell are auto-curried. This means that if the function application does not provide the required number of parameters, it will return a function which will accept the rest of the parameters one at a time, just like the Lodash and Ramda curry functions do. Another cool thing in Haskell is that partial application looks like non-partial application and curried function calls aren’t ugly, since the separator for parameters is a space.

times a b = a * b

times 1 2

double = times 2

Effective Functional JavaScript Recipe

Use and abuse closures, partial application and currying.

This will enable you to compose your functions and write code that is extremely terse without losing readability.

Remember to give this post some 💚 if you liked it. Follow me Hugo Di Francesco or @hugo__df for more JavaScript content :).

Tags