How can I pre-set arguments in JavaScript function call? (Partial Function Application)
JavascriptFunctional ProgrammingJavascript Problem Overview
I am trying to write a JavaScript function that will return its first argument(function) with all the rest of its arguments as preset parameters to that function.
So:
function out(a, b) { document.write(a + " " + b); }function setter(...) {...}
setter(out, "hello")("world"); setter(out, "hello", "world")();
Would output "hello world" twice. for some implementation of setter
I ran into an issue with manipulating the arguments array on my first try, but it seems there would be a better way to do this.
Javascript Solutions
Solution 1 - Javascript
First of all, you need a partial - there is a difference between a partial and a curry - and here is all you need, without a framework:
function partial(func /*, 0..n args */) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var allArguments = args.concat(Array.prototype.slice.call(arguments));
return func.apply(this, allArguments);
};
}
Now, using your example, you can do exactly what you are after:
partial(out, "hello")("world");
partial(out, "hello", "world")();
// and here is my own extended example
var sayHelloTo = partial(out, "Hello");
sayHelloTo("World");
sayHelloTo("Alex");
The partial()
function could be used to implement, but is not currying. Here is a quote from a blog post on the difference:
> Where partial application takes a function and from it builds a function which takes fewer arguments, currying builds functions which take multiple arguments by composition of functions which each take a single argument.
Hope that helps.
Solution 2 - Javascript
Is curried javascript what you're looking for?
Solution 3 - Javascript
Using Javascript's apply()
, you can modify the function prototype
Function.prototype.pass = function() {
var args = arguments,
func = this;
return function() {
func.apply(this, args);
}
};
You can then call it as out.pass('hello','world')
apply
takes an array for 2nd argument/parameter.
arguments
is property available inside function which contains all parameters in array like structure.
One other common way to do this is to use bind
loadedFunc = func.bind(this, v1, v2, v3);
then
loadedFunc() === this.func(v1,v2,v3);
this kinda suffice, even though little ugly.
Solution 4 - Javascript
If you use Dojo you just call dojo.hitch() that does almost exactly what you want. Almost — because it can be used to pack the context as well. But your example is first:
dojo.hitch(out, "hello")("world");
dojo.hitch(out, "hello", "world")();
As well as:
var A = {
sep: ", ",
out: function(a, b){ console.log(a + this.sep + b); }
};
// using functions in context
dojo.hitch(A, A.out, "hello")("world");
dojo.hitch(A, A.out, "hello", "world")();
// using names in context
dojo.hitch(A, "out", "hello")("world");
dojo.hitch(A, "out", "hello", "world")();
dojo.hitch() is the part of the Dojo Base, so as soon as you included dojo.js it is there for you.
Another general facility is available in dojox.lang.functional.curry module (documented in http://lazutkin.com/blog/2008/jan/12/functional-fun-javascript-dojo/">Functional fun in JavaScript with Dojo — just look on this page for "curry"). Specifically you may want to look at curry(), and partial().
curry() accumulates arguments (like in your example) but with one difference: as soon as the arity is satisfied it calls the function returning the value. Implementing your example:
df.curry(out)("hello")("world");
df.curry(out)("hello", "world");
Notice that the last line doesn't have "()" at the end — it is called automatically.
partial() allows to replace arguments at random:
df.partial(out, df.arg, "world")("hello");
Solution 5 - Javascript
You could use Function.prototype.bind()
for this. It's an ES5 addition.
In addition to the common usecase of setting a function's context (this
value), it can also set partial arguments.
function out(a, b) {
document.write(a + " " + b);
}
function setter(func) {
return func.bind.apply(func, [window].concat([].slice.call(arguments).slice(1)));
}
setter(out, "hello")("world");
setter(out, "hello", "world")();
My setter
function is actually very simple. The longest part is just getting the list of arguments. I'll break up the code like this:
func.bind.apply(func, [window].concat([].slice.call(arguments).slice(1)))
func.bind.apply( ) // need to use apply to pass multiple arguments as an array to bind()
func, // apply needs a context to be run in
[window].concat( ) // pass an array of arguments to bind(), starting with window, to be the global context
[].slice.call(arguments).slice(1) // convert the arguments list to an array, and chop off the initial value
It's supported in these browsers: Chrome 7+, Firefox 4+, IE9+. MDN (linked at the beginning) has a polyfill though.
Solution 6 - Javascript
Easy way to achieve presetting of parameters without calling the function straight away (when e.g. awaiting user confirmation), is to "dress" the function in another anonymous function.
Instead of: exportFile(docType)
Do: function(){ return exportFile(docType) }
Solution 7 - Javascript
** EDIT: See Jason Bunting's response. This answer actually shows a sub-par way of chaining numerous out calls, not a single out-call with presets for some of the arguments. If this answer actually helps with a similar problem, you should be sure to make use of apply and call as Jason recommends, instead of the obscure way to use eval that I thought up. **
Well... your out will actually write "undefined" a lot in this... but this should be close to what you want:
function out(a, b) {
document.write(a + " " + b);
}
function getArgString( args, start ) {
var argStr = "";
for( var i = start; i < args.length; i++ ) {
if( argStr != "" ) {
argStr = argStr + ", ";
}
argStr = argStr + "arguments[" + i + "]"
}
return argStr;
}
function setter(func) {
var argStr = getArgString( arguments, 1 );
eval( "func( " + argStr + ");" );
var newSettter = function() {
var argStr = getArgString( arguments, 0 );
if( argStr == "" ) {
argStr = "func";
} else {
argStr = "func, " + argStr;
}
return eval( "setter( " + argStr + ");" );
}
return newSettter;
}
setter(out, "hello")("world");
setter(out, "hello", "world")();
I'd probably move the code in getArgString into the setter function itself though... a little bit safer since I used 'eval's.
Solution 8 - Javascript
Using closures is another option. Use functions that return other functions!