Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript function challenge add(1,2) and add(1)(2) both should return 3

A friend of mine challenged me to write a function that works with both of these scenarios

add(2,4)  // 6
add(2)(4) // 6

My instinct was the write an add() function that returns itself but I'm not sure I'm heading in the right direction. This failed.

function add(num1, num2){
    if (num1 && num2){
        return num1 + num2;
    } else {
        return this;
    }
}

alert(add(1)(2));

So I started reading up on functions that return other functions or return themselves.

  • http://davidwalsh.name/javascript-functions
  • JavaScript: self-calling function returns a closure. What is it for?
  • JavaScript: self-calling function returns a closure. What is it for?

I am going to keep trying, but if someone out there has a slick solution, I'd love to see it!

like image 500
MicFin Avatar asked Jul 09 '15 00:07

MicFin


2 Answers

I wrote a curried function whose valueOf() method and function context (this) are bound with the sum no matter how many arguments are passed each time.

/* add function */
let add = function add(...args) {
  const sum = args.reduce((acc, val) => acc + val, this);
  const chain = add.bind(sum);
  chain.valueOf = () => sum;
  return chain;
}.bind(0);

/* tests */
console.log('add(1, 2) = ' + add(1, 2));
console.log('add(1)(2) = ' + add(1)(2));
/* even cooler stuff */
console.log('add(1, 2)(3) = ' + add(1, 2)(3));
console.log('add(1, 2, 3)(4, 5)(6) = ' + add(1, 2, 3)(4, 5)(6));
/* retains expected state */
let add7 = add(7);
console.log('let add7 = add(7)');
console.log('add7(3) = ' + add7(3));
console.log('add7(8) = ' + add7(8));

The reason why both mechanisms are required is because the body of add() must use the called function's bound context in order to access the sum of the intermediate partial application, and the call site must use the valueOf() member (either implicitly or explicitly) in order to access the final sum.

like image 122
Patrick Roberts Avatar answered Oct 09 '22 14:10

Patrick Roberts


There is an article on Dr.Dobs Journal about "Currying and Partial Functions in JavaScript" which describes exactly this problem.

One solution found in this article is:

// a curried add
// accepts partial list of arguments
function add(x, y) {
     if (typeof y === "undefined") { // partial
        return function (y) {
              return x + y;
        };
     }
   // full application
   return x + y;
}
like image 39
andih Avatar answered Oct 09 '22 16:10

andih