Spices and other ingredients of a great curry
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.
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.
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 styleconst imperative = list[0];
// declarative styleconst 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.
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.
Recursion in JavaScript with ES6, destructuring and rest/spread_The latest ECMA standard for JavaScript (ECMAScript 6) makes JavaScript more readable by encouraging a more declarative…_hackernoon.com
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 :).
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;});
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(); // 2counter.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 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 callsomeFunc(1, 2); // 3
// different ways to apply a functionsomeFunc(1, 2); // 3someFunc.apply(this, [ 1, 2 ]); // 3someFunc.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 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 oncefunctionalMap(x => x * 2, [ 1, 2, 3 ]);functionalMap(x => x * 2, [ 2, 3, 5 ]);
// Example 2// Let's apply them one at a timeconst 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.
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 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.
Currying in JavaScript_A technique using partial evaluation_medium.com
Currying in JavaScript_I've been thinking a lot lately about functional programming, and I thought it might be kind of fun to walk through the…_kevvv.in
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
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 :).