48

I need a js sum function to work like this:

sum(1)(2) = 3
sum(1)(2)(3) = 6
sum(1)(2)(3)(4) = 10 
etc.

I heard it can't be done. But heard that if adding + in front of sum can be done. Like +sum(1)(2)(3)(4).
Any ideas of how to do this?

Eric
  • 95,302
  • 53
  • 242
  • 374
Yaroslav Yakovlev
  • 6,303
  • 6
  • 39
  • 59
  • See also [here](http://stackoverflow.com/a/18067040/1048572) for some more explanation – Bergi May 29 '16 at 22:05
  • 6
    **Variadic functions and currying are incompatible.** Just don't do it! The accepted answer is a dirty hack. Use semi-variadic functions instead, where the variadic arguments are passed within an array. –  Jun 24 '16 at 21:42
  • 1
    The functions in JavaScript are variadic by default, so hacking the language in a such way is harmful, because it may confuse newbies that getting multi-arity fns like this is the way, while it's definitely not. There are more smart ways to learn how can we use `valueOf ` method. – Bogdan Slovyagin Apr 18 '17 at 12:35

19 Answers19

84

Not sure if I understood what you want, but

function sum(n) {
  var v = function(x) {
    return sum(n + x);
  };

  v.valueOf = v.toString = function() {
    return n;
  };

  return v;
}

console.log(+sum(1)(2)(3)(4));

JsFiddle

adiga
  • 34,372
  • 9
  • 61
  • 83
Rafael
  • 18,349
  • 5
  • 58
  • 67
  • 1
    what does the + before the function call means? – Dan Apr 29 '11 at 14:00
  • +1 Great solution, was working on it myself :). I also added a jsFiddle to your answer if you do not mind. – kapa Apr 29 '11 at 14:01
  • 11
    The + casts the result of sum() to number. In that case JavaScript calls the .valueOf method of an object. Since I'm returning anonymous function, this helps to convert it to primitive type. As you can see, I overwrote the native .valueOf with my own. – Rafael Apr 29 '11 at 14:02
  • 1
    Btw Firefox dont need + to perform casting :-). you can call it without and it will work. – Yaroslav Yakovlev Apr 29 '11 at 14:04
  • 6
    @Yaroslav: It will work if the result is converted to string (eg. when using alert()). When using native console.log, you will see function () { return n + x; } in the console. – Rafael Apr 29 '11 at 14:06
  • @Rafael: The `valueOf` gets called only when `n` is `undefined`? If so, can you explain the reasoning behind this? Also, the `+` you have added before invocation of `sum', does it typecast after all the invocations are complete? – darKnight May 10 '18 at 20:03
  • 2
    @maverick no, `valueOf` is called by JavaScript internals, e.g. when running the unary `+` operator. As per ES5 specs, the `+` runs the abstract `ToNumber`, which in turn calls abstract `ToPrimitive` which calls internal method ` [[DefaultValue]]` which finally calls `valueOf`. You can follow the call chain starting from [here](https://es5.github.io/#x11.4.6) – Rafael May 15 '18 at 10:05
  • @maverick yes, the cast is done at the very end. I think the behavior can be explained using the [operator precedence table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) – Rafael May 15 '18 at 10:12
35

This is an example of using empty brackets in the last call as a close key (from my last interview):

sum(1)(4)(66)(35)(0)()

function sum(firstNumber) {
    let accumulator = firstNumber;
    return function adder(nextNumber) {
        if (nextNumber === undefined) {
            return accumulator;
        } 
    
        accumulator += nextNumber;
        return adder;
    }
}

console.log(sum(1)(4)(66)(35)(0)());
yury.hrynko
  • 410
  • 4
  • 11
13

I'm posting this revision as its own post since I apparently don't have enough reputation yet to just leave it as a comment. This is a revision of @Rafael 's excellent solution.

function sum (n) {
    var v = x => sum (n + x);
    v.valueOf = () => n; 
    return v;
}

console.log( +sum(1)(2)(3)(4) ); //10

I didn't see a reason to keep the v.toString bit, as it didn't seem necessary. If I erred in doing so, please let me know in the comments why v.toString is required (it passed my tests fine without it). Converted the rest of the anonymous functions to arrow functions for ease of reading.

adiga
  • 34,372
  • 9
  • 61
  • 83
Brad
  • 359
  • 4
  • 12
  • `toString` means that `console.log("The result is " + sum(1) + "!")` works properly – Eric May 30 '16 at 10:33
  • 1
    `console.log("The result is " + sum(1) + "!")` returns the string `​The result is 1!` even when the `toString` is omitted from the `v.valueOf` statement in the `sum` function, as I have done above. Still not clear on what adding `toString` would accomplish that isn't already happening. – Brad May 30 '16 at 21:09
9

New ES6 way and is concise.

You have to pass empty () at the end when you want to terminate the call and get the final value.

const sum= x => y => (y !== undefined) ? sum(x + y) : x;

call it like this -

sum(10)(30)(45)();
Sean
  • 323
  • 2
  • 14
Deepak K
  • 329
  • 3
  • 11
  • Nice! A variant which allows multiple arguments at each step, like `sum (3, 4, 5) (22) (2, 6) () //=> 42`, could look like this: `const sum = (...xs) => (...ys) => ys .length ? sum (...xs, ...ys) : xs .reduce ((a, b) => a + b, 0)`. – Scott Sauyet Nov 24 '21 at 15:52
8

Here is a solution that uses ES6 and toString, similar to @Vemba

function add(a) {
  let curry = (b) => {
    a += b
    return curry
  }
  curry.toString = () => a
  return curry
}

console.log(add(1))
console.log(add(1)(2))
console.log(add(1)(2)(3))
console.log(add(1)(2)(3)(4))
Artokun
  • 553
  • 5
  • 9
6

Another slightly shorter approach:

const sum = a => b => b? sum(a + b) : a;

console.log(
  sum(1)(2)(),
  sum(3)(4)(5)()
);
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • your code returns an error for this case: sum(1)(2)(0)() – yury.hrynko Mar 31 '21 at 11:16
  • @yury.hrynko kind of, 0 is falsy thus it also terminates the chain. Adding 0 is senseless anyways, though if you need this you can replace `b?` with `b !== undefined ?` – Jonas Wilms Mar 31 '21 at 17:59
5

Here's a solution with a generic variadic curry function in ES6 Javascript, with the caveat that a final () is needed to invoke the arguments:

const curry = (f) =>
   (...args) => args.length? curry(f.bind(0, ...args)): f();
const sum = (...values) => values.reduce((total, current) => total + current, 0)
curry(sum)(2)(2)(1)() == 5 // true

Here's another one that doesn't need (), using valueOf as in @rafael's answer. I feel like using valueOf in this way (or perhaps at all) is very confusing to people reading your code, but each to their own.

The toString in that answer is unnecessary. Internally, when javascript performs a type coersion it always calls valueOf() before calling toString().


// invokes a function if it is used as a value
const autoInvoke = (f) => Object.assign(f, { valueOf: f } );

const curry = autoInvoke((f) =>
   (...args) => args.length? autoInvoke(curry(f.bind(0, ...args))): f());

const sum = (...values) => values.reduce((total, current) => total + current, 0)
curry(sum)(2)(2)(1) + 0 == 5 // true
user1034533
  • 1,054
  • 9
  • 9
3

Try this, this is more flexible to handle any type of input. You can pass any number of params and any number of paranthesis.

function add(...args) {
    function b(...arg) {
        if (arg.length > 0) {
            return add(...[...arg, ...args]);
        }

        return [...args, ...arg].reduce((prev,next)=>prev + next);
    }

    b.toString = function() {
        return [...args].reduce((prev,next)=>prev + next);
    }

    return b;
}

// Examples
console.log(add(1)(2)(3, 3)());
console.log(+add(1)(2)(3)); // 6
console.log(+add(1)(2, 3)(4)(5, 6, 7)); // 28
console.log(+add(2, 3, 4, 5)(1)());    // 15
shekhardtu
  • 4,335
  • 7
  • 27
  • 34
2

Here's a more generic solution that would work for non-unary params as well:

const sum = function (...args) {
  let total = args.reduce((acc, arg) => acc+arg, 0)
  function add (...args2) {
    if (args2.length) {
      total = args2.reduce((acc, arg) => acc+arg, total)
      return add
    }
    return total
  }

  return add
}

document.write( sum(1)(2)() , '<br/>') // with unary params
document.write( sum(1,2)() , '<br/>') // with binary params
document.write( sum(1)(2)(3)() , '<br/>') // with unary params
document.write( sum(1)(2,3)() , '<br/>') // with binary params
document.write( sum(1)(2)(3)(4)() , '<br/>') // with unary params
document.write( sum(1)(2,3,4)() , '<br/>') // with ternary params
UtkarshPramodGupta
  • 7,486
  • 7
  • 30
  • 54
2

Try this

function sum (...args) {
  return Object.assign(
    sum.bind(null, ...args),
    { valueOf: () => args.reduce((a, c) => a + c, 0) }
  )
}

console.log(+sum(1)(2)(3,2,1)(16))

Here you can see a medium post about carried functions with unlimited arguments

https://medium.com/@seenarowhani95/infinite-currying-in-javascript-38400827e581

Victor Molina
  • 2,353
  • 2
  • 19
  • 49
2

ES6 way to solve the infinite currying. Here the function sum will return the sum of all the numbers passed in the params:

const sum = a => b => b ? sum(a + b) : a

sum(1)(2)(3)(4)(5)() // 15
Gaurav
  • 857
  • 3
  • 21
  • 29
1

Here is another functional way using an iterative process

const sum = (num, acc = 0) => {
    if !(typeof num === 'number') return acc;
    return x => sum(x, acc + num)
}

sum(1)(2)(3)()

and one-line

const sum = (num, acc = 0) => !(typeof num === 'number') ? acc : x => sum(x, acc + num)

sum(1)(2)(3)()
Aliaksandr Sushkevich
  • 11,550
  • 7
  • 37
  • 44
1
function add(a) {
    let curry = (b) => {
        a += b
        return curry;
    }
    curry[Symbol.toPrimitive] = (hint) => {
        return a;
    }
    return curry
}

console.log(+add(1)(2)(3)(4)(5));        // 15
console.log(+add(6)(6)(6));              // 18
console.log(+add(7)(0));                 // 7
console.log(+add(0));                    // 0
Shubham Jain
  • 307
  • 1
  • 8
1

we can also use this easy way.

function sum(a) {
    return function(b){
      if(b) return sum(a+b);
      return a;
    }
}

console.log(sum(1)(2)(3)(4)(5)());
0

You can make use of the below function

function add(num){
   add.sum || (add.sum = 0) // make sure add.sum exists if not assign it to 0
   add.sum += num; // increment it
   return add.toString = add.valueOf = function(){ 
      var rtn = add.sum; // we save the value
      return add.sum = 0, rtn // return it before we reset add.sum to 0
   }, add; // return the function
}

Since functions are objects, we can add properties to it, which we are resetting when it's been accessed.

Amit Joki
  • 58,320
  • 7
  • 77
  • 95
-1

To make sum(1) callable as sum(1)(2), it must return a function.

The function can be either called or converted to a number with valueOf.

function sum(a) {

   var sum = a;
   function f(b) {
       sum += b;
       return f;
    }
   f.toString = function() { return sum }
   return f
}
Cleb
  • 25,102
  • 20
  • 116
  • 151
Vemba
  • 40
  • 3
-1
   function sum(a){
    let res = 0;
    function getarrSum(arr){
            return arr.reduce( (e, sum=0) =>  { sum += e ; return sum ;} )
     }

    function calculateSumPerArgument(arguments){
            let res = 0;
            if(arguments.length >0){

            for ( let i = 0 ; i < arguments.length ; i++){
                if(Array.isArray(arguments[i])){
                    res += getarrSum( arguments[i]);
                }
                else{
                  res += arguments[i];
                }
             }
          }
            return res;
     }
    res += calculateSumPerArgument(arguments);



    return function f(b){
        if(b == undefined){
            return res;
        }
        else{
            res += calculateSumPerArgument(arguments);
            return f;
        }
    }

}
  • I have written generic function .. it will run for any combination of number //and array. For calling Ex. sum(1)(2)() or sum(1)(2)(12,[3,4])() or sum(1,2,3,[12,4])(4,5,[1,2,3])(23)() – Manoj Patial Mar 30 '18 at 10:48
-1
let add = (a) => {
  let sum = a;
  funct = function(b) {
    sum += b;
    return funct;
  };

  Object.defineProperty(funct, 'valueOf', {
    value: function() {
      return sum;
    }
  });
  return funct;
};


console.log(+add(1)(2)(3))
-1

After looking over some of the other solutions on here, I would like to provide my two solutions to this problem.

Currying two items using ES6:

const sum = x => y => (y !== undefined ) ? +x + +y : +x
sum(2)(2) // 4

Here we are specifying two parameters, if the second one doesnt exist we just return the first parameter.

For three or more items, it gets a bit trickier; here is my solution. For any additional parameters you can add them in as a third

const sum = x => (y=0) => (...z) => +x + +y + +z.reduce((prev,curr)=>prev+curr,0)
sum(2)()()//2
sum(2)(2)()//4
sum(2)(2)(2)//6
sum(2)(2)(2,2)//8

I hope this helped someone

Dr Gaud
  • 97
  • 3
  • The whole idea is to make it work without writing any case. So this solution has no advantages over the more generic ones and has a disadvantage of a lot of code. – Yaroslav Yakovlev Mar 09 '21 at 18:35
  • ah okay, I think ill go back and relearn this better then...thanks for that man – Dr Gaud Mar 12 '21 at 15:11