I got contacted by someone who had been stumped by an interview question about JavaScript. The question involved currying functions, and although I’ve mentioned curried functions and JavaScript before I haven’t really done more than assume people know what that is. This person had found my web site but remained confused.
Since I’m on vacation and I was drinking my morning coffee and feeling generally benevolent to the world, I wrote up a reply. This is that reply, recycled as a post and expanded.
Problem:
Given the following information:
var add = function(a, b) { return a+b; };
var add2 = mybind(add, 2);
add2(5); // outputs 7
Write the mybind
function.
Discussion and Solution
First of all, to keep things as clear as possible I’m going to follow Douglas Crockford’s suggestion in “JavaScript: The Good Parts” — a book which I strongly recommend — and put a space before the parentheses ( )
when they aren’t being used to invoke a function. So if you see a(b)
in my code without a space it means a
is a function and b
is an argument.
So, let’s run through the code from the problem.
var add = function (a, b) {
return a+b;
};
This one’s easy enough — we’re defining a function which adds two values together and returns the result.
var add2 = mybind(add, 2);
add2(5); // outputs 7
Now it gets tricky. Looking at the last line first, I’m going to assume that “outputs 7” means that we want it to return 7, so in my final code — which I’m testing with NodeJS — I’m going to do:
console.log( add2(5) );
I’ll consider the other possibility later.
Now add2(5)
returns 7
, so add2
must be a function which takes a single argument and returns a single argument.
But add2
is created by the mysterious mybind
function in the line before. So now we know that the mybind
function itself must return a function. We also know what kind of function it returns — one which takes a single argument, and returns a single argument. Finally, we know that mybind takes two arguments.
So let’s write a skeleton version of “mybind” which does all that, but leave all the insides empty:
var mybind = function (a, b) {
return function (x) {
// ??? compute r for result
return r
};
};
Clear so far? That’s most of the battle, all we need to do now is work out what goes in the // ???
line, given a
, b
and x
.
At this point I was still having trouble seeing the right answer before finishing my morning coffee, so I decided to give myself some help: I renamed the function arguments to remind me of what the example values are in the problem! So instead of calling the first parameter to mybind a
, I called it add
; I called the variable which is going to hold the value 2 in the problem two
, and so on. Once I did that, I had this:
var mybind = function (add, two) {
return function (five) {
// ??? compute seven for result
return seven;
};
};
At this point, the right answer will hopefully leap out at you the way it leapt out at me: remembering that add
is a function which adds, seven = add(five, two)
.
So, the final code is:
var mybind = function (add, two) {
return function (five) {
var seven = add(five, two);
return seven;
};
};
Of course, we can now rename the function arguments back to something general, and eliminate the unnecessary temporary variable. Here’s the final result, which I fed to NodeJS to check that it worked:
var add = function (a, b) {
return a + b;
};
var mybind = function (fn, a) {
return function (b) {
return fn(b, a);
};
};
var add2 = mybind(add, 2);
console.log(add2(5));
The only remaining issue is whether add2(5)
is really supposed to return 7 for the assumed REPL loop to display, or whether it was actually supposed to output 7 directly. That’s simple enough, though — it’s just a matter of changing mybind to (say) call console.log
instead of returning the value.
Let’s Make It More Confusing
The functional programmers of the JavaScript community will find the above solution rather unsatisfactory. It’s far too verbose! If functional programming is about anything, it’s about making your code look like scary opaque math in order to save a few keystrokes.
Fortunately, ECMAScript now has a wonderful tool for doing that: arrow functions. Instead of the boring old:
function (a, b) { return a + b; };
we can type:
(a, b) => a + b;
Half as much typing! So let’s use this new syntax to rewrite our mybind
function:
let mybind = (fn, a) => (b) => fn(b, a);
Isn’t that so much better for confusing inexperienced programmers?
As you can probably tell, I’m not a fan of this shorthand syntax. I feel that it obscures function bodies, making them implicit, and at a glance it looks too similar to a simple expression. I’m sure mathematicians love it, but I came to computer science via science and it makes my brain hurt.
Now With Added Curry
Functional programmers are also likely unsatisfied with my mentioning currying at the start of the article, because technically the problem is about partial application via function binding. So let me show why I thought “currying” when I saw the problem.
Here’s our problem and solution in shiny new ECMA syntax:
let add = (a, b) => a + b;
let mybind = (fn, a) => (b) => fn(b, a);
let add2 = mybind(add, 2);
console.log(add2(5));
Currying is taking a function with multiple arguments, and reducing it to a sequence of applications of functions with a single argument. So f(x,y,z) becomes f’(x)(y)(z), where f’ is the curried version of f. f’(x) returns an unnamed function, and when that function is called with the value y
that returns a function, and when that function is called with the value z
it returns the answer.
We can then go further and say that f(x,y,z) is curry(f)(x)(y)(z) where curry is the currying function which takes f and returns f’.
When I saw the problem, I imagined a curried add function:
let cadd = (a) => (b) => add(b, a);
The imaginary curried add cadd(a)
would return a function which, when applied to b
, would call add(a, b)
and return the result.
The cadd
function is similar to mybind, except that mybind
takes an additional argument for the function to apply instead of add
:
let mybind = (fn, a) => (b) => fn(b, a);
So let’s finish the currying process. Given that we know what curried add would look like, we can write the currying function which, given add
, returns cadd
:
let curry = (f) => (a) => (b) => f(b, a);
And sure enough:
let add = (a, b) => a + b;
let curry = (f) => (a) => (b) => f(a, b);
console.log( curry(add)(5)(2) ); // outputs 7
If you’re totally lost, don’t worry too much — this is pretty much useless, except for passing a computer science degree or terrifying people in technical interviews.