Preserving arity in function wrappers

# Bruno Jouhier (12 years ago)

The classical pattern for function wrappers is the following

function wrap(fn) {
  return function F() { ... var result = fn.apply(this, arguments); ... return result; };
}

This pattern has one problem though: the returned function has a different arity than the original function. This causes problems with libraries that test fn.length; for example with express and moccha in node.js.

I found two ways to restore the arity:

  • inside wrap, call F.toString(), replace F() by F(a1, a2, ...), eval and return the new function.
  • dynamically create one variant of the wrap function for each arity. Similar but the substitution and eval is done on wrap rather than on F. A bit more complex to set up but more efficient.

I find it rather clumsy to have to call eval to solve this. Maybe there is a better solution without eval but if there isn't it may be interesting to improve the language for this. Some ideas (probably naive):

  • make the length property of functions writable.
  • enhance the Function(arg1, arg1, ..., functionBody) constructor to accept a function as functionBody, or introduce a Function(nArgs, bodyFn) variant.

WDYT?

# Rick Waldron (12 years ago)

They won't be writable, but they will be configurable: rwaldron/tc39-notes/blob/master/es6/2013-07/july-25.md#consensusresolution-3, which meets the need you've described.

# Bruno Jouhier (12 years ago)

Configurable will do it. Thanks.

# Andrea Giammarchi (12 years ago)

when I've seen this ...

On Sat, Sep 14, 2013 at 1:49 PM, Bruno Jouhier <bjouhier at gmail.com> wrote:

  • inside wrap, call F.toString(), replace F() by F(a1, a2, ...), eval and return the new function.

I wondered why this would not be enough ...

function wrap(Function) {
  for(var i = 0, a = []; i < Function.length; a[i] = 'a' + i++);
  return eval('('.concat(
    'function ',
    Function.name || '',
    '(', a.join(','), ') {',
      // do some magic here
      'return Function.apply(this, arguments);',
    '})'
  ));
}

function first(one, two, three) {
  return one + two + three;
}

wrap(first)(1, 2, 3); // 6

... if you parse to string you loose the scope plus most testing frameworks out there use the with statement, eval, Function, etc ... so why do you feel like eval is not a solution? Is is a JSLint VS testing code the problem or something else I cannot think about right now?

Cheers