Closures and currying
In this article, we’ll explore some powerful functional programming concepts: higher-order functions, closures and currying. We’ll use javascript for illustration.
In functional programming, higher-order functions are nothing but functions that take in functions as arguments and/or return functions as return values.
Let’s consider a javascript function that adds two numbers:
function naive_add(x, y){
return x + y;
}
In the above example, the naive_add
function took two arguments upfront.
We could also visualize add
as a higher-order function that takes in one argument (x
), and returns a function that adds its input (y
) to x
.
function add(x) {
return function(y) {
return x + y;
}
}
The inner function is a closure. Closures are functions that keep track of variables from their containing scopes. In this case, the closure keeps track of the variable x
, even after it is returned to the outside world.
This enables us to write some elegant code like:
let increment = add(1);
let decrement = add(-1);
increment(4); //5
decrement(4); //3
We expressed the increment
and decrement
functions in terms of the add
higher-order function.
Had we stuck with naive_add
function, the increment
and decrement
function would’ve looked more verbose:
function naive_increment(y){
return add(1, y);
}
function naive_decrement(y){
return add(-1, y);
}
Best of both worlds
Wouldn’t it be awesome if we had the simplicity of naive_add
and expressiveness of increment
/decrement
functions?
Enter currying. As you’d have already guessed, it has nothing to do with Paneer butter masala.
Currying is a technique of binding a function to a subset of its arguments. It is named after the logician Haskell Curry. In javascript, the bind
method is used to curry functions:
let increment = naive_add.bind(null, 1);
let decrement = naive_add.bind(null, -1);
increment(4); //5
decrement(4); //3
This means that we donot have to explicity hand-code higher-order functions. bind
does that for us under the hood.
Note that the first argument to the bind
function is the receiver, which is consulted in case the function that we’re currying has references to this
.
Let’s conclude by looking at a more tastier example of currying:
function pizza_maker(base, cheese, ...toppings){
return `Place the ${base} crust on a greased and dusted pan.
Grate some ${cheese} cheese on it. Add ${toppings.join(',')}.
Top it up with even more ${cheese} cheese.`;
}
let thincrust = pizza_maker.bind(null, "thin and crispy");
let margherita = thincrust("mozarella", "tomato", "basil");
//Place the thin and crispy crust on a greased and dusted pan. Grate some mozarella cheese on it. Add tomato,basil. Top it up with even more
//mozarella cheese.
let deepdish = thincrust("mozarella+cheddar", "tomato", "jalapenos", "olives");
//Place the thin and crispy crust on a greased and dusted pan. Grate some mozarella+cheddar cheese on it. Add tomato,jalapenos,olives. Top it up with
//even more mozarella+cheddar cheese.