53

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.

Jason Bunting
  • 58,249
  • 14
  • 102
  • 93
AlexH
  • 2,827
  • 7
  • 31
  • 30
  • I recommend the `curry` function of [Lodash](http://lodash.com/docs#curry). BTW I recommend the whole library, it is very useful. – Gábor Imre Oct 20 '14 at 11:25
  • [bind: partially applied functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind#partially_applied_functions) – djvg Nov 07 '22 at 12:35

8 Answers8

97

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.

starball
  • 20,030
  • 7
  • 43
  • 238
Jason Bunting
  • 58,249
  • 14
  • 102
  • 93
3

Is curried javascript what you're looking for?

wpercy
  • 9,636
  • 4
  • 33
  • 45
Peter Bailey
  • 105,256
  • 31
  • 182
  • 206
  • I don't think he needs currying, per se - see http://stackoverflow.com/questions/218025/what-is-the-difference-between-currying-and-partial-application – Jason Bunting Nov 26 '08 at 17:39
  • 2
    Link no longer works. Is this the same page? http://www.crockford.com/javascript/www_svendtofte_com/code/curried_javascript/index.html – ZAD-Man Jul 02 '14 at 19:38
3

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.

Muhammad Umer
  • 17,263
  • 19
  • 97
  • 168
  • 1
    i think you should add a proper explanation how this works; An example of OP's function should be included too. – Atural Dec 15 '17 at 07:43
  • 1
    A lot of people are against modifications of base prototypes for trivial purposes, but this one seems ideal, maintaining readability throughout your code. – Regular Jo Dec 15 '17 at 17:48
  • personally i think i've just used bind for most cases though, ill add an example of how – Muhammad Umer Dec 19 '17 at 19:50
2

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 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");
Eugene Lazutkin
  • 43,776
  • 8
  • 49
  • 56
1

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.

Scimonster
  • 32,893
  • 9
  • 77
  • 89
1

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) }

Kalana
  • 5,631
  • 7
  • 30
  • 51
marjon4
  • 332
  • 3
  • 9
  • The only thing I would add to this answer (the simplest compared with all the other ones above) is to add binding at the end, as in function(){return exportFile(docType)}.bind(this) to safely transfer the context to the function inside – nenea Dec 13 '21 at 22:46
0

** 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.

Illandril
  • 656
  • 4
  • 11
  • Right idea, but horrible implementation. See my answer - you do not need to build things up as strings, JavaScript has call() and apply() functions that make things like this easier. – Jason Bunting Nov 26 '08 at 17:45
  • Huh... never heard of call or apply functions before - those do make it easier. – Illandril Nov 26 '08 at 18:20
  • I would suggest spending some time reading up on them - while they have a very niche use, they are quite useful for those niche uses! – Jason Bunting Nov 26 '08 at 18:50
-1

Using closures is another option. Use functions that return other functions!

Ray
  • 142
  • 1
  • 5