0
\$\begingroup\$

Introduction

I have some JavaScript code that uses Array.prototype.map to map an array of functions fns to their return values:

const fns = [() => 1, () => 2];
const result = fns.map(
  fn => fn()
);
console.log(result); // => [1, 2]

Challenge

The argument to map above is fn => fn(). The challenge is to rewrite this function using point-free style. Your solution should work when it replaces the third line of the above program:

const fns = [() => 1, () => 2];
const result = fns.map(
  /* your solution should work when inserted here */
);
console.log(result); // => [1, 2]

fn => fn() is not a valid solution because it defines a parameter named fn. Writing a function in point-free style requires writing it without any variable or parameter names.

Note that map will pass three arguments to your solution function: currentValue: () => T, index: number, and array: Array<() => T>. The function for the solution must return the result of calling the currentValue function, so hard-coding 1 and 2 will not help. The functions being mapped over are guaranteed to ignore their arguments and their this value, so calling the function in any way will work.

Scoring

The best answer is the one that is represented by the fewest tokens when lexed. The ANTLR 4 JavaScript lexer is the official lexer for the sake of objectivity, but if you know how a lexer works you should be able to estimate how many tokens some JavaScript needs without having to read that link. For example, fn => fn() is composed of five tokens: fn => fn ( ).

However, I personally think that figuring out any answer is the fun part, so if this problem interests you, try to solve it yourself before scrolling down and seeing the answer.

\$\endgroup\$
2
\$\begingroup\$

Based on the answer by Rory O'Kane:

10 tokens: Number.call.bind(String.call)

Try it online!

It is trivial, since Number.call, String.call, Object.call, Function.call, (function () {}).call, Function.prototype.call are all the same.

If Bind Operator Proposal is accepted, this could be simplified to:

6 tokens: ::Function.call.call

Babel

BTW, if eval is allowed eval("Number.call.bind(String.call)") only use 4 tokens.

\$\endgroup\$
1
\$\begingroup\$

This combination of Function.prototype.call and Function.prototype.bind works:

Function.prototype.call.call.bind(Function.prototype.call)

16 tokens: Function . prototype . call . call . bind ( Function . prototype . call )

const fns = [() => 1, () => 2];
const result = fns.map(
  Function.prototype.call.call.bind(Function.prototype.call)
);
console.log(result); // => [1, 2]

Explanation

Function.prototype.call is not a solution on its own because map will call call with its this value set to undefined. call’s purpose is to call the function in this, so when this is undefined a TypeError is raised.

Similarly, Function.prototype.call.bind(Function.prototype) is insufficient because the call function is called with this set to Function.prototype, and the function you want called set to the first argument. Calling Function.prototype() with any arguments returns undefined, so the function will return undefined instead of 1 or 2.

My solution works by using the second call to make the fn argument the this of the first call, like this:

fn => Function.prototype.call.call(fn)

Now the first call can do what it is made for, calling the argument in its this value.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.