Red
Red

Reputation: 674

Curried functions: how to optimize them

I'm relatively new to functional programming and libraries such as ramda.js but one thing I found very useful is the possibility of currying functions.

Using curried functions I write very often things as the following

const myFun = R.curry(
  (arg1, arg2) => {
    let calculated = anotherFun(arg1)
      //do something with calculated and arg2
    return calculated * 5 + arg2
  }
)

const anotherFun = (arg) => {
  console.log("calling anotherFun");
  return arg + 1
}

var partial = myFun(1)

console.log(partial(2))
console.log(partial(3))
<script src="//cdn.jsdelivr.net/ramda/0.22.1/ramda.min.js"></script>

but clearly in this situation anotherFun is called every time I call partial even if in arg1 and as a consequence calculated are always the same.

Is there a way to optimize this behaviour and call anotherFun only when its args change?

The only way that crosses my mind is this

const myFun = R.curry(
  (calculated, arg2) => {
    return calculated * 5 + arg2
  }
)

const anotherFun = (arg) => {
  console.log("calling anotherFun");
  return arg + 1
}

var calculated = anotherFun(1)
var partial = myFun(calculated)

console.log(partial(2))
console.log(partial(3))
<script src="//cdn.jsdelivr.net/ramda/0.22.1/ramda.min.js"></script>

but in this way I have to change the arguments passed to myFun and this complicates the external API

Upvotes: 0

Views: 191

Answers (3)

Kirill Shumilov
Kirill Shumilov

Reputation: 307

How about useWith and memoize from Ramda?

const myFun = R.useWith(
  (a, b) => a * 5 + b,
  [R.memoize(anotherFun), R.identity]
);

Upvotes: 0

Scott Sauyet
Scott Sauyet

Reputation: 50807

@Bergi is right that Ramda will not offer you any help with this. If you want a Ramda-style result, where you can call with one parameter to get a function back or both to get the result you can do this:

const myFun = function(arg1, arg2) {
  let calculated = anotherFun(arg1);
  const newFunc = arg2 => {
    return calculated * 5 + arg2
  };
  return (arguments.length < 2) ? newFunc : newFunc(arg2);
};

const with3 = myFun(3);
//: calling anotherFun

with3(1); //=> 21
with3(2); //=> 22
with3(4); //=> 23


myFun(2, 7);
//: calling anotherFun
//=> 22 

myFun(2, 8);
//: calling anotherFun
//=> 23 

This comes at the cost of not being able to use ES2015 arrow functions. But it might be worth it to you.

You can also rework this slightly to not build the internal function if both parameters are supplied, if that is important to you.

Upvotes: 1

Bergi
Bergi

Reputation: 665040

If you do the currying manually like this

const myFun = arg1 => arg2 => {
  let calculated = anotherFun(arg1)
  // do something with calculated and arg2
  return calculated * 5 + arg2
};

you can also make this optimisation:

const myFun = arg1 => {
  let calculated = anotherFun(arg1);
  return arg2 => {
    // do something with calculated and arg2
    return calculated * 5 + arg2
  };
};

I don't think Ramda will help you here with anything; and JavaScript compilers certainly are not doing this kind of optimisation.

Upvotes: 3

Related Questions