0

I trying to make next with closure:

function func(number) {
    var result = number;

    var res = function(num) {
        return result + num;
    };
    return res;
}

var result = func(2)(3)(4)(5)(3);
console.log(result); // 17

I need to receive 2 + 3 + 4 + 5 + 3 = 17 But I got an error: Uncaught TypeError: number is not a function

SiJey
  • 169
  • 1
  • 4
  • 9
  • 9
    This hurts my brain.. You can either return a function or a number, not both.. – Mike Christensen Aug 05 '13 at 19:40
  • @MikeChristensen: Actually, you could return both by checking `arguments.length`. – SLaks Aug 05 '13 at 19:43
  • 4
    @SLaks - Oh and put a `()` at the end of the chain? Yea, that'd be a clever way to annoy your co-workers. – Mike Christensen Aug 05 '13 at 19:44
  • 2
    Is there any reason why you can't use `func(2, 3, 4, 5, 3)` and rework your function to accommodate that setup? Although you have a simple example, it kinda makes more sense to head this way instead of continually invoking a function?... – Ian Aug 05 '13 at 19:48
  • Just for interesting, I trying to do this throught closure. – SiJey Aug 05 '13 at 19:50
  • 1
    possible duplicate of [Javascript sum function](http://stackoverflow.com/questions/5832891/javascript-sum-function) – Paulo Almeida Aug 05 '13 at 20:17

7 Answers7

9

You somehow have to signalize the end of the chain, where you are going to return the result number instead of another function. You have the choice:

  • make it return a function for a fixed number of times - this is the only way to use the syntax like you have it, but it's boring. Look at @PaulS' answer for that. You might make the first invocation (func(n)) provide the number for how many arguments sum is curried.
  • return the result under certain circumstances, like when the function is called with no arguments (@PaulS' second implementation) or with a special value (null in @AmoghTalpallikar's answer).
  • create a method on the function object that returns the value. valueOf() is suited well because it will be invoked when the function is casted to a primitive value. See it in action:

    function func(x) {
        function ret(y) {
            return func(x+y);
        }
        ret.valueOf = function() {
            return x;
        };
        return ret;
    }
    
    func(2) // Function
    func(2).valueOf() // 2
    func(2)(3) // Function
    func(2)(3).valueOf() // 5
    func(2)(3)(4)(5)(3) // Function
    func(2)(3)(4)(5)(3)+0 // 17
    
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
8

You're misusing your functions.

func(2) returns the res function.
Calling that function with (3) returns the number 5 (via return result + num).

5 is not a function, so (4) gives an error.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • @user2056866 It's hard to see what practical use this would have so it's likely that this is homework. Are you trying to make a recursive function? You're a bit off if so. – JJJ Aug 05 '13 at 19:46
  • Just for interesting, I trying to do this throught closure. – SiJey Aug 05 '13 at 19:49
  • @user2056866: Try my answer. its not exactly how you want it but its almost the same. – Amogh Talpallikar Aug 05 '13 at 20:18
3

Well, the (2)(3) part is correct. Calling func(2) is going to return you res, which is a function. But then, calling (3) is going to return you the result of res, which is a number. So the problem comes when you try to call (4).

For what you're trying to do, I don't see how Javascript would predict that you're at the end of the chain, and decide to return a number instead of a function. Maybe you could somehow return a function that has a "result" property using object properties, but mostly I'm just curious about why you're trying to do things this way. Obviously, for your specific example, the easiest way would just be adding the numbers together, but I'm guessing you're going a bit further with something.

Katana314
  • 8,429
  • 2
  • 28
  • 36
  • Yes, it is just for interesting. But I still can`t understand how will be right. – SiJey Aug 05 '13 at 19:47
  • Like I said, your entire method of calling it this way is weird. Would it be more acceptable to return an object with chain-callable functions? ie, `func().addmore(3).addmore(5).addmore(7).getResult()` ? This is normally what JQuery does. – Katana314 Aug 05 '13 at 20:11
0

If you want to keep invoking it, you need to keep returning a function until you want your answer. e.g. for 5 invocations

function func(number) {
    var result = number,
        iteration = 0,
        fn = function (num) {
            result += num;
            if (++iteration < 4) return fn;
            return result;
        };
    return fn;
}
func(2)(3)(4)(5)(3); // 17

You could also do something for more lengths that works like this

function func(number) {
    var result = number,
        fn = function () {
            var i;
            for (i = 0; i < arguments.length; ++i)
                result += arguments[i];
            if (i !== 0) return fn;
            return result;
        };
    return fn;
}
func(2)(3, 4, 5)(3)(); // 17
Paul S.
  • 64,864
  • 9
  • 122
  • 138
0

I flagged this as a duplicate, but since this alternative is also missing from that question I'll add it here. If I understand correctly why you would think this is interesting (having an arbitrary function that is applied sequentially to a list of values, accumulating the result), you should also look into reduce:

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

a = [2, 3, 4, 5, 3];

b = a.reduce(sum);
Paulo Almeida
  • 7,803
  • 28
  • 36
0

Another solution could be just calling the function without params in order to get the result but if you call it with params it adds to the sum.

function add() {
  var sum = 0;
  var closure = function() {
    sum = Array.prototype.slice.call(arguments).reduce(function(total, num) {
      return total + num;
    }, sum);
    return arguments.length ? closure : sum;
  };
  return closure.apply(null, arguments);
}

console.log(add(1, 2, 7)(5)(4)(2, 3)(3.14, 2.86)); //  function(){}
console.log(add(1, 2, 7)(5)(4)(2, 3)(3.14, 2.86)()); // 30;
cstuncsik
  • 2,698
  • 2
  • 16
  • 20
0

We can make light work of it using a couple helper functions identity and sumk.

sumk uses a continuation to keep a stack of the pending add computations and unwinds the stack with 0 whenever the first () is called.

const identity = x => x

const sumk = (x,k) =>
  x === undefined ? k(0) : y => sumk(y, next => k(x + next))

const sum = x => sumk(x, identity)

console.log(sum())                // 0
console.log(sum(1)())             // 1
console.log(sum(1)(2)())          // 3
console.log(sum(1)(2)(3)())       // 6
console.log(sum(1)(2)(3)(4)())    // 10
console.log(sum(1)(2)(3)(4)(5)()) // 15
Mulan
  • 129,518
  • 31
  • 228
  • 259