Currying JavaScript Functions
Currying a function is the act of transforming a function that takes n arguments into a function that takes n - 1 arguments. Essentially, currying is the act of binding the first argument of a function to a specific value, creating a new function that takes the remaining arguments. Here's a simple example:
function add(x, y) { return x + y; } // produces 7 add(3, 4); // produces a function with x bound to 3 that takes y as an argument var add3 = curry(add, 3); // produces 7 add3(4);
In this example, we curry the add(x, y)
function to produce the add3(y)
function that always adds 3 to its only argument. While this may seem useless initially, currying is very useful in the context of Ajax callbacks. For example, say you want to add a constant amount 4 to a counter every time a button is clicked? Just curry!
button.onclick = curry(callback(this, addToTotal), 4);
Thanks to the functional parts of JavaScript and the ability to access JavaScript function arguments as an array with the arguments
variable, implementing the curry
function is easy, as this recipe illustrates:
function curry(method) { var curried = []; for (var i = 1; i < arguments.length; i++) { curried.push(arguments[i]); } return function() { var args = []; for (var i = 0; i < curried.length; i++) { args.push(curried[i]); } for (var i = 0; i < arguments.length; i++) { args.push(arguments[i]); } return method.apply(null, args); } }
This curry
function is more flexible than the formal definition of the term: you can bind as many arguments as you want when you call curry
, like this example, where we bind the first two arguments of a function to produce a function that only takes one argument:
function add(x, y, z) { return x + y + z; } var add3 = curry(add, 2, 1); // binds x to 2 and y to 1 add3(5); // produces 8
Comments
var curried = Array.prototype.slice.apply(arguments, [1]);
Another "problem" here, the variable i is declared twice in the same function scope, that's not really harming browsers, but that's a bad practice.
<pre>
var toAdd = Number(document.getElementById('userIn').value);
alert("add3 returns: " + add3(toAdd));
</pre>
or else the interpreter will treat the user input as a string (which is reasonable but not what we want).</p>
<p>Otherwise this is a great example, I had no idea that js could do such ruby-like stuff. ;-)</p>
See Ecma 262-3 sections 12.6.3 and 12.2.
Ah ?! You are really sure ? Do you have any paragraph in ECMA262 I should read again ?
As far I know there is not such thing as "scope of the for loop", but let me try to proove it to you.
<script type="text/javascript">
alert(myVar + ' ' + typeof myVar);
</script>
This will broke with an error saying something pretty close to : myVar is not defined
Now, let's do it again but we add a for loop where we will declare the variable
<script type="text/javascript">
alert(myVar + ' ' + typeof myVar);
for ( var myVar = 0; myVar < 5; myVar++ ) {}
</script>
Now it does not break anymore, however the alert reports : undefined undefined
The "var myVar" within the for loop does not really looks declared only within the "for loop scope" as you stated.
Basically because the interpretor has transformed the code to this :
<script type="text/javascript">
var myVar;
alert(myVar + ' ' + typeof myVar);
for ( myVar = 0; myVar < 5; myVar++ ) {}
</script>
Let's keep testing your assertion. If you are right, the following source should break or at least reports "undefined undefined"
<script type="text/javascript">
for ( var i = 0; i < 2; i++ ) {}
for ( var k = 0; k < 1; k++ )
{
alert(i + ' ' + typeof i);
}
</script>
As expected it does not break, it alerts "2 number" which definitively means there is no such thing as the "scope of the for loop".
So as I said, your example is declaring twice the same variable "i". It does not really do any harm, but that is just a bad practice !!!
var i;
for (i = 0; i < curried.length; i++) { args.push(curried[i]); }
for (i = 0; i < arguments.length; i++) { args.push(arguments[i]); }
for (var i = 0; i < curried.length; i ) { args.push(curried[i]); }
for (var i = 0; i < arguments.length; i ) { args.push(arguments[i]); }
Actually I would go further and write:
for (var i = 0, len = curried.length; i < len; i ) { args.push(curried[i]); }
for (var i = 0, len = arguments.length; i < len; i ) { args.push(arguments[i]); }
In your example, if you forget to declare "var i", the i suddenly becomes a global. There are no bad side effects to declaring "var i" twice within the function as long as you are aware of javascript scoping rules. This is also good for when you or someone else maintaining the code, for example, deletes the first loop and the "var i" without realising that "i" is used in a second loop.
Declaring a variable close to where it is being used is generally considered good practice. I agree that the scoping rules in javascript can be a little surprising but I would argue that the following is absolutely good practice:
for (var i = 0; i < curried.length; i++) { args.push(curried[i]); }
for (var i = 0; i < arguments.length; i++) { args.push(arguments[i]); }
Actually I would go further and write:
for (var i = 0, len = curried.length; i < len; i++) { args.push(curried[i]); }
for (var i = 0, len = arguments.length; i < len; i++) { args.push(arguments[i]); }
In your example, if you forget to declare "var i", the i suddenly becomes a global. There are no bad side effects to declaring "var i" twice within the function as long as you are aware of javascript scoping rules. This is also good for when you or someone else maintaining the code, for example, deletes the first loop and the "var i" without realising that "i" is used in a second loop. It's not such an issue for compiled languages where a compiler will catch this kind of mistake.
Write a Comment