In Douglas Crockford's book "Javascript: The Good Parts" he provides code for a curry
method which takes a function and arguments and returns that function with the arguments already added (apparently, this is not really what "curry" means, but is an example of "partial application"). Here's the code, which I have modified so that it works without some other custom code he made:
Function.prototype.curry = function(){
var slice = Array.prototype.slice,
args = slice.apply(arguments),
that = this;
return function() {
// context set to null, which will cause `this` to refer to the window
return that.apply(null, args.concat(slice.apply(arguments)));
};
};
So if you have an add
function:
var add = function(num1, num2) {
return num1 + num2;
};
add(2, 4); // returns 6
You can make a new function that already has one argument:
var add1 = add.curry(1);
add1(2); // returns 3
That works fine. But what I want to know is why does he set this
to null
? Wouldn't the expected behavior be that the curried method is the same as the original, including the same this
?
My version of curry would look like this:
Function.prototype.myCurry = function(){
var slice = [].slice,
args = slice.apply(arguments),
that = this;
return function() {
// context set to whatever `this` is when myCurry is called
return that.apply(this, args.concat(slice.apply(arguments)));
};
};
Example
(Here is a jsfiddle of the example)
var calculator = {
history: [],
multiply: function(num1, num2){
this.history = this.history.concat([num1 + " * " + num2]);
return num1 * num2;
},
back: function(){
return this.history.pop();
}
};
var myCalc = Object.create(calculator);
myCalc.multiply(2, 3); // returns 6
myCalc.back(); // returns "2 * 3"
If I try to do it Douglas Crockford's way:
myCalc.multiplyPi = myCalc.multiply.curry(Math.PI);
myCalc.multiplyPi(1); // TypeError: Cannot call method 'concat' of undefined
If I do it my way:
myCalc.multiplyPi = myCalc.multiply.myCurry(Math.PI);
myCalc.multiplyPi(1); // returns 3.141592653589793
myCalc.back(); // returns "3.141592653589793 * 1"
However, I feel like if Douglas Crockford did it his way, he probably has a good reason. What am I missing?
Reason 1 - not easy to provide a general solution
The problem is that your solution is not general. If the caller doesn't assign the new function to any object, or assigns it to a completely different object, your
multiplyPi
function will stop working:So, neither Crockford's nor your solution can assure that the function will be used correctly. Then it may be easier to say that the
curry
function works only on "functions", not "methods", and setthis
tonull
to force that. We might only speculate though, since Crockford doesn't mention that in the book.Reason 2 - functions are being explained
If you asking "why Crockford didn't use this or that" - the very likely answer is: "It wasn't important in regard to the demonstrated matter." Crockford uses this example in the chapter Functions. The purpose of the sub-chapter
curry
was:Finetuning this for a general usage with objects was not purpose of this chapter. As it is problematic if not even impossible (see Reason 1), it was more educational to put there just
null
instead if putting there something which could raise questions if it actually works or not (didn't help in your case though :-)).Conclusion
That said, I think you can be perfectly confident in your solution! There's no particular reason in your case to follow Crockfords' decision to reset
this
tonull
. You must be aware though that your solution only works under certain circumstances, and is not 100% clean. Then clean "object oriented" solution would be to ask the object to create a clone of its method inside itself, to ensure that the resultant method will stay within the same object.