Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to curry a function across an unknown number of parameters

Say I have a function called multiplyDivide

If I were to call multiplyDivide(2)(3)(4)(6) it would be equivalent to 2 * 3 / 4 * 6.

Update: Is it possible to write a function like this if I don't know in advance how many parameters I will be taking? For example, I could have multiplyDivide(1)(2) or multiplyDivide(1)(2)(3)(4)...(n-1)(n)

like image 502
TheRealFakeNews Avatar asked Aug 28 '16 02:08

TheRealFakeNews


People also ask

How do you make a function take any number of arguments?

So if you'd like to make a function that accepts any number of positional arguments, use the * operator.

How would you implement currying for any functions?

As my bonus tip, here is a modern way of implementing currying using the ES6 arrow function. It helps you write less code: const sendRequest = greet => name => message => `${greet} ${name}, ${message}` sendRequest('Hello')('John')('Please can you add me to your Linkedin network? ')

How do you write a curry function?

The following basic example uses currying. function multiply(a, b, c) { return a * b * c; } function multiply_curried(a) { return function (b) { return function (c) { return a * b * c } } } let res = multiply(1, 2, 3); console. log(res); let mc1 = multiply_curried(1); let mc2 = mc1(2); let res2 = mc2(3); console.

Can you give an example of a curry function and why this syntax offers an advantage?

Curry functions are neat when used to carry containers of reusable code. Basically you take a function with multiple arguments and you know that one of those arguments will have specific value but the other is undecided.


2 Answers

It's sort of possible but you need to define the terminating condition because the problem is essentially the same problem as writing a recursive function. The function needs a way to tell whether it should return a function or a value.

How you signal the need for values is up to you. One way of doing it is to check if an argument is passed:

// Using add instead of multiplyDivide to simplify example:

function add (num) {
    function adder (n) {
        if (n !== undefined) {
            num += n;
            return adder;
        }
        else { // terminate
            return num;
        }
    }
    return adder;
}

Now you can do:

var sum = add(1)(2)(3)(4)();

Otherwise it would return a function which you can keep calling:

var x = add(1)(2)(3)(4);
x = x(5)(6)(7);
x = x(8)(9)(10);

var sum = x();

Since in js functions are objects, you can also implement the value getter as a static method. It won't be purely functional but makes the "API" a bit more explicit and easier to read:

function add (num) {
    function adder (n) {
        num += n;
        return adder;
    }
    adder.value = function(){
        return num
    };
    return adder;
}

Which would allow you to do:

var sum = add(1)(2)(3)(4).value();

You can even get fancy by overriding the built-in .valueOf() and .toString() methods:

function add (num) {
    function adder (n) {
        num += n;
        return adder;
    }
    adder.valueOf = function(){
        return num
    };
    adder.toString = function(){
        return '' + num
    };
    return adder;
}

Which would allow you to do:

var sum = add(1)(2)(3)(4) + 5; // results in 15
var txt = add(1)(2)(3)(4) + "hello"; // results in "10hello"

The key here is that you need a way to tell the function to stop returning functions.

like image 115
slebetman Avatar answered Oct 22 '22 11:10

slebetman


Using a functional approach, you can create a function that "curries" arguments for another function. You will need a way to tell the function to return the value, so in this case, calling the function without passing any arguments will return the result:

function curry(fn, ...values) {
    return (...next) => (next.length) ? curry(fn, ...values, ...next) : fn(...values);
}

The cool thing about this function is that you can pass multiple arguments and/or keep invoking the function (1)(2, 3, 4)(5).

Here's a couple of examples:

function curry(fn, ...values) {
  return (...next) => (next.length) ? curry(fn, ...values, ...next) : fn(...values);
}

function multiplyDivide(...args) {
  return args.reduce((total, next, i) => (i % 2) ? (total / next) : (total * next), args.shift());
}

let x = curry(multiplyDivide)(2)(3, 4)(6)();
console.log(x);

let y = curry(multiplyDivide)(5, 4, 2)(3);
y = y(3, 5)(1)();
console.log(y);

Of course, this example does hint at just simply overloading the multiplyDivide function and passing your values to that when you're ready:

function multiplyDivide(...args) {
  return args.reduce((total, next, i) => (i % 2) ? (total / next) : (total * next), args.shift());
}

const values = [5, 4, 2, 3, 3];
values.push(5, 1);

console.log(multiplyDivide(...values));
like image 30
KevBot Avatar answered Oct 22 '22 12:10

KevBot