Ramda: How to modify function parameters without `R.wrap`?

311 views Asked by At

How to override a methods parameter defaults, without wrap (since it's about to be removed). Consider the following.

let foo = (bar, { logToConsole = false }) => {
  if (logToConsole) console.log(bar);
  return bar;
};

// I want foo to always log, but I want to leave the option open to 
//   override that on a per instance basis (which excludes doing a 
//   `R.partial`). Looking at the methods available, `wrap` seems to 
//   do what I want...

foo = R.wrap(foo, (fn, bar, options) => {
  options = R.merge({ logToConsole: true }, options);
  return fn(bar, options);
}

So, reassigning foo, with the default flipped. Every time I call foo after, it will have the default changed.

Since wrap is deprecated, what should I be using instead?

I followed along this thread, but didn't see how to solve this pattern with the examples there.

Thanks for the help!

2

There are 2 answers

4
Scott Sauyet On BEST ANSWER

That discussion involved David showing that every use I would come up with for wrap could be rewritten more simply, especially with ES6 handy.

I'm not sure whether he's right, but it's not far off. I've been using something like wrap for years, doing AOP-style work in JS, and I still do, just not using it inside Ramda. Whether they all can be rewritten in a different manner, writing them all consistently is useful to me, but David's point was a good one.

Your case can, I believe, be rewritten pretty straightforwardly:

const foo2 = (bar, options) => foo(bar, R.merge({logToConsole: true}, options));

foo2(42);
// logs 42
//=> 42
foo2(99, {logToConsole: false});
// (doesn't log)
//=> 99 

(I'm assuming that command in the original is supposed to be bar. Is that correct?)

Update

If you're looking to reassign foo, you can wrap that one up like this:

foo = ((orig) => 
  (bar, options) => orig(bar, R.merge({logToConsole: true}, options))
)(foo);

This would have the same behavior as foo2 above. (Obviously it won't work with your const declaration of foo.)

2
davidchambers On

I suggest a different approach. I assume the options hash is always the last argument. If this is the case one can write a decorator:

function logLastArg(f) {
  return R.curryN(f.length, function() {
    console.log(R.last(arguments));
    return R.apply(f, arguments);
  });
}

function query(queryString, dbOpts) {
  return ['foo', 'bar', 'baz'];
}

var queryWithLogging = logLastArg(query);

query('SELECT username FROM users', {name: 'default'});
// => ['foo', 'bar', 'baz']

queryWithLogging('SELECT username FROM users', {name: 'default'});
// logs "{name: 'default'}"
// => ['foo', 'bar', 'baz']