views:

1936

answers:

4

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.

+2  A: 

Is curried javascript what you're looking for?

Peter Bailey
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
+1  A: 
Eugene Lazutkin
A: 

** 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
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
Huh... never heard of call or apply functions before - those do make it easier.
Illandril
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
+13  A: 

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.

Jason Bunting
Could you edit this? func.apply( this, allArguments) needs to be return func.apply(this, allArguments)
AlexH
Done - wasn't really looking beyond your non-returning example function, so I neglected it originally. Thanks.
Jason Bunting
I'm voting your answer up just because I've searched for something to do this before.
Jarett