6

I'm learning JavaScript, and I recently came across a practice problem that asked me to construct a function that could create outputs as follows:

var threeSum= sum(3);
threeSum //3
threeSum(4) //7
threeSum(4)(3) //10
threeSum(4)(3)(7) //17
threeSum(4)(3)(7)()(2) //19
threeSum - 2 //1
threeSum + 2 //5

I assume currying is involved, and I think I have a basic grasp of how currying works in the simple form of something like

a=>b=>c=> a+b+c

but I have no notion of how I would create a curried function able to handle an indeterminate number of inputs, nor how to make it such that it could result in a variable that can act as both a value and a function.

Any insight is appreciated! I just need a push in the right direction -- at this point I don't even know what I'm looking for anymore.

Brad
  • 359
  • 4
  • 12
  • 1
    A function can't return a value which can act as a number and as a function simultaneously. But you can return a function which will be coerced to the desired number. – Oriol May 29 '16 at 20:50
  • @Oriol: What's the difference between acting as a number and being coercible to a number? – Eric May 29 '16 at 21:55
  • Apologies for the duplicate post. To be fair, though, the original post's "JavaScript sum function" title is incredibly generic, and has little to do with the actual operation of the code in question, just its intended function. – Brad May 29 '16 at 22:21
  • @Eric I mean `var obj = {valueOf:()=>42}` will become `42` when coerced to a number, but `obj !== 42`, `typeof obj !== 'number'`, etc. It won't be `42` itself. – Oriol May 29 '16 at 22:51
  • @Brad: _"recently came across a practice problem"_ - can you link to that problem, to aid finding future dupes? – Eric May 30 '16 at 00:45
  • 1
    @Eric http://www.codewars.com/kata/539a0e4d85e3425cb0000a88/train/javascript I'll put it in the question, as well. – Brad Jun 03 '16 at 19:00

2 Answers2

6

The trick here is that you need to define valueOf, which allows javascript to interpret an object (like a function) as a value:

var valueAndCallable = function(x) {
    var res = function(a) { return a + x };
    res.valueOf = function() { return x; };
    return res;
};

var v = valueAndCallable(1)
console.log(v);      // function ... -
console.log(+v);     // 1 - calls .valueOf()
console.log(1 + v);  // 2 - calls .valueOf()
console.log(v(3));   // 4

For currying, you just want to make res() also return a valueAndCallable.

Eric
  • 95,302
  • 53
  • 242
  • 374
  • this is just a dirty hack! –  May 29 '16 at 20:29
  • @Iven: But the question is asking for a dirty hack! – Eric May 29 '16 at 20:29
  • OK, now @Brad knows that this solution is kind of a hack and should be used with caution. –  May 29 '16 at 20:39
  • Brad asks for how to _"hold a function and a [numeric] value in a variable"_. `valueOf` is the only way to achieve this. No unhacky solution can exist – Eric May 29 '16 at 20:41
  • `For currying, you just want to make res() also return a valueAndCallable.` that interesting part, can you impliment that? – layonez May 29 '16 at 21:24
  • @LayonezWronskey: Op wanted a push in the right direction, not a complete answer. – Eric May 29 '16 at 21:56
  • As long as I've got something that helps me understand the concept in a way I can replicate and apply it on my own in the future, I'm happy. Thank you for all the responses and dialogue!! – Brad May 29 '16 at 22:18
1

As stated in the comments you can't define a variable that acts like a number and a function simultaneously.

To curry variadic functions you have to pass the arity explicitly:

const curryN = n => f => {
  let next = (m, acc) => x => m > 1 ? next(m - 1, acc.concat([x])) : f(...acc, x);
  return next(n, []);
};

const sum = (...args) => args.reduce((acc, x) => acc + x, 0);

sum(1, 2, 3, 4, 5); // 15
curryN(5)(sum)(1)(2)(3)(4)(5); // 15

let sum3 = curryN(3)(sum);
sum3(1)(2)(3); // 6

let sum5plusX = curryN(2)(sum)(5);
sum5plusX(6); // 11

I would recommend not to use variadic functions at all. Use Array.reduce instead. Here's an interesting tutorial about currying. It's an in-depth topic.