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?
From MDN:
Hence, if the method is in non-strict mode and the first argument is
null
orundefined
,this
inside of that method will referenceWindow
. In strict mode, this isnull
orundefined
. I've added a live example on this Fiddle.Furthermore passing in
null
orundefined
does not do any harm in case the function does not referencethis
at all. That's probably why Crockford usednull
in his example, to not overcomplicate things.There is not really a reason. Probably he wanted to simplify, and most functions that make sense to be curried or partially applied are not OOP-methods that use
this
. In a more functional style thehistory
array that is appended to would be another parameter of the function (and maybe even a return value).Yes, your implementation makes much more sense, however one might not expect that a partially applied function still needs to be called in the correct context (as you do by re-assigning it to your object) if it uses one.
For those, you might have a look at the
bind
method of Function objects for partial application including a specificthis
-value.Reader beware, you're in for a scare.
There's a lot to talk about when it comes to currying, functions, partial application and object-orientation in JavaScript. I'll try to keep this answer as short as possible but there's a lot to discuss. Hence I have structured my article into several sections and at the end of each I have summarized each section for those of you who are too impatient to read it all.
1. To curry or not to curry
Let's talk about Haskell. In Haskell every function is curried by default. For example we could create an
add
function in Haskell as follows:Notice the type signature
Int -> Int -> Int
? It means thatadd
takes anInt
and returns a function of typeInt -> Int
which in turn takes anInt
and returns anInt
. This allows you to partially apply functions in Haskell easily:The same function in JavaScript would look ugly:
The problem here is that functions in JavaScript are not curried by default. You need to manually curry them and that's a pain. Hence we use partial application (aka
bind
) instead.Lesson 1: Currying is used to make it easier to partially apply functions. However it's only effective in languages in which functions are curried by default (e.g. Haskell). If you have to manually curry functions then it's better to use partial application instead.
2. The structure of a function
Uncurried functions also exist in Haskell. They look like functions in "normal" programming languages:
You can convert a function in its curried form to its uncurried form and vice versa using the
uncurry
andcurry
functions in Haskell respectively. An uncurried function in Haskell still takes only one argument. However that argument is a product of multiple values (i.e. a product type).In the same vein functions in JavaScript also take only a single argument (it just doesn't know it yet). That argument is a product type. The
arguments
value inside a function is a manifestation of that product type. This is exemplified by theapply
method in JavaScript which takes a product type and applies a function to it. For example:Can you see the similarity between the above line in JavaScript and the following line in Haskell?
Ignore the assignment to
main
if you don't know what it's for. It's irrelevant apropos to the topic at hand. The important thing is that the tuple(2, 3)
in Haskell is isomorphic to the array[2, 3]
in JavaScript. What do we learn from this?The
apply
function in JavaScript is the same as function application (or$
) in Haskell:We take a function of type
a -> b
and apply it to a value of typea
to get a value of typeb
. However since all functions in JavaScript are uncurried by default theapply
function always takes a product type (i.e. an array) as its second argument. That is to say that the value of typea
is actually a product type in JavaScript.Lesson 2: All functions in JavaScript only take a single argument which is a product type (i.e. the
arguments
value). Whether this was intended or happenstance is a matter of speculation. However the important point is that you understand that mathematically every function only takes a single argument.Mathematically a function is defined as a morphism:
a -> b
. It takes a value of typea
and returns a value of typeb
. A morphism can only have one argument. If you want multiple arguments then you could either:b
is another morphism). This is currying. Haskell does this.a
to be a product of multiple types (i.e.a
is a product type). JavaScript does this.Out of the two I prefer curried functions as they make partial application trivial. Partial application of "uncurried" functions is more complicated. Not difficult, mind you, but just more complicated. This is one of the reasons why I like Haskell more than JavaScript: functions are curried by default.
3. Why OOP matters not
Let's take a look at some object-oriented code in JavaScript. For example:
Now you might wonder how is this object-oriented. It looks more like functional code. After all you could do the same thing in Haskell:
Nevertheless the above code is object-oriented. The array literal is an object which has a method
filter
which returns a new array object. Then we simply access thelength
of the new array object.What do we learn from this? Chaining operations in object-oriented languages is the same as composing functions in functional languages. The only difference is that the functional code reads backwards. Let's see why.
In JavaScript the
this
parameter is special. It's separate from the formal parameters of the function which is why you need to specify a value for it separately in theapply
method. Becausethis
comes before the formal parameters, methods are chained from left-to-right.If
this
were to come after the formal parameters the above code would probably read as:Not very nice is it? Then why do functions in Haskell read backwards? The answer is currying. You see functions in Haskell also have a "
this
" parameter. However unlike in JavaScript thethis
parameter in Haskell is not special. In addition it comes at the end of the argument list. For example:The
filter
function takes a predicate function and athis
list and returns a new list with only the filtered elements. So why is thethis
parameter last? It makes partial application easier. For example:In JavaScript you would write:
Now which one would you choose? If you're still complaining about reading backwards then I have news for you. You can make Haskell code read forwards using "backward application" and "backward composition" as follows:
Now you have the best of both worlds. Your code reads forwards and you get all the benefits of currying.
There are a lot of problems with
this
that don't occur in functional languages:this
parameter is specialized. Unlike other parameters you can't simply set it to an arbitrary object. Hence you need to usecall
to specify a different value forthis
.null
as the first parameter ofbind
. Similarly forcall
andapply
.Object-oriented programming has nothing to do with
this
. In fact you can write object-oriented code in Haskell as well. I would go as far as to say that Haskell is in fact an object-oriented programming language, and a far better one at that than Java or C++.Lesson 3: Functional programming languages are more object-oriented than most mainstream object-oriented programming languages. In fact object-oriented code in JavaScript would be better (although admittedly less readable) if written in a functional style.
The problem with object-oriented code in JavaScript is the
this
parameter. In my humble opinion thethis
parameter shouldn't be treated any differently than formal parameters (Lua got this right). The problem withthis
is that:this
like other formal parameters. You have to usecall
instead.this
tonull
inbind
if you wish to only partially apply a function.On a side note I just realized that every section of this article is becoming longer than the preceding section. Hence I promise to keep the next (and final) section as short as possible.
4. In defense of Douglas Crockford
By now you must have picked up that I think that most of JavaScript is broken and that you should shift to Haskell instead. I like to believe that Douglas Crockford is a functional programmer too and that he is trying to fix JavaScript.
How do I know that he's a functional programmer? He's the guy that:
new
keyword (a.k.aObject.create
). If you don't already do then you should stop using thenew
keyword.Anyway, I think Crockford nullified
this
in thecurry
function because he knows how badthis
is. It would be sacrilege to set it to anything other thannull
in a book entitled "JavaScript: The Good Parts". I think he's making the world a better place one feature at a time.By nullifying
this
Crockford is forcing you to stop relying on it.Edit: As Bergi requested I'll describe a more functional way to write your object-oriented
Calculator
code. We will use Crockford'scurry
method. Let's start with themultiply
andback
functions:As you can see the
multiply
andback
functions don't belong to any object. Hence you can use them on any array. In particular yourCalculator
class is just a wrapper for list of strings. Hence you don't even need to create a different data type for it. Hence:Now you can use Crockford's
curry
method for partial application:Next we'll create a
test
function tomultiplyPi
by one and to go back to the previous state:If you don't like the syntax then you could switch to LiveScript:
The
bindState
function is thebind
function of the state monad. It's defined as follows:So let's put it to the test:
See the demo here: http://jsfiddle.net/5h5R9/
BTW this entire program would have been more succinct if written in LiveScript as follows:
See the demo of the compiled LiveScript code: http://jsfiddle.net/5h5R9/1/
So how is this code object oriented? Wikipedia defines object-oriented programming as:
According to this definition functional programming languages like Haskell are object-oriented because:
Functor
is a superclass ofApplicative
.The above code is also object-oriented. The object in this case is
myCalc
which is simply an array. It has two functions associated with it:multiply
andback
. However it doesn't own these functions. As you can see the "functional" object-oriented code has the following advantages:So I hope that helped.
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.