3

I want a 'curry' like function - this kind of thing

function invoker (fn) {
    var slice = Array.prototype.slice,
        args = slice.apply(arguments, [1]);
    return function () {
       return fn.apply(null, args);
    };
}

But I want the user to be able to do

invoker(f)

or

invoker(foo.bar)

I cant find the correct magic incantation to do this. All the examples I see require the scope object to be passed in separately; which is error prone and not natural. IE

invokerx(foo.bar, foo)

IS there anyway to do this? I dont mind having 2 different functions

invokeG(f)
invokeO(foo.bar)

EDIT : clarification

'f' is a global scope function
'foo' is an object
'bar is a method on that object

IE I want this curry tool to work with'free' functions as well as with object functions.

Having to go

<curry function>(foo.bar,foo)

seems kinda clunky, I have have to say 'foo' twice

2
  • Saying "I want the user to be able to do invoker(f) or invoker(foo.bar)" doesn't describe what you want. Please describe in words the problem you're trying to solve. EDIT: Maybe you want the user to be able to bind a context to the function? Commented Oct 14, 2011 at 15:27
  • What are f and foo.bar in this case? Commented Oct 14, 2011 at 15:27

2 Answers 2

4

The first argument of apply is the this that is available to the function. Since you're passing null, this will get mapped to window.

The following modification will work:

function invoker (fn, target) {
    var slice = Array.prototype.slice,
        args = slice.apply(arguments, [2]);

    if(typeof fn === 'string')
        fn = (target || window)[fn];

    return function () {
       return fn.apply(target || null, args);
    };
}

// Either will work:
invoker(foo.bar, foo);
invoker('bar', foo);

// Any of these will work:
invoker(escape, null);
invoker(escape, window);
invoker('escape', null);
invoker('escape', window);

UPDATE Added a slight change to add support for name passing so you don't have to pass the object name twice.

UPDATE 2 Since we're currying, we need there to always be two arguments.

Sign up to request clarification or add additional context in comments.

6 Comments

right, but this requires the caller to use 2 different call signatures. My feeling is that invoker(foo.bar, foo) is redundant , I already said foo once
You know that, and I know that, but JavaScript doesn't know that. I'll add an alternate solution that doesn't require multiple instances of 'foo', but it will be less efficient.
cmon brian, one more heroic effort and I am sure you can find a really nice solution. You are getting pretty close with the 'invoker('bar', foo), but we are still not quite there :-)
@pm100: Short of doing something horrible, like invoker('foo.bar'), that's as close as you can get
First off, it would only work if foo was a global variable. Second, it would require use of eval (scary/slow) or string parsing (slow). In practice, there are many examples of curry functions bound to objects and they all need a second parameter which often results in typing the object name twice. It's just how JavaScript works.
|
2

You can't. When you do invoker(foo.bar), invoker has no way of knowing that the argument is a member function of foo. Functions do not store their "owner", and invoker is just passed a reference to a function.

The closest you can get is to use function.bind(), like this:

invoker(foo.bar.bind(foo))

2 Comments

I guess thats it, i wanted to find the 'owner' of the function. I am surprised that apply doesnt have some variant that allows this.
Actually, the .bind function is a currying function so you don't really need invoker here. var boundBar = foo.bar.bind(foo) is all you need. Otherwise you're currying a curried function. See: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.