Lopatin Lopatin - 7 days ago 6
Javascript Question

Idiomatic Ramda for generating higher order functions?

My goal is to create a custom map function that first needs to filter the list to remain, for example, only even items before invoking the supplied function on every item. I do need the function to be curried and for the first parameter to be the function, not the list. I believe the signature would look like this:

(a -> b) -> [a] -> [b]


There are of course many ways to do this. Here is what my first attempt looked like.

var isEven = x => x % 2 === 0;

var filterEvensMap = R.curry((fn, items) => R.map(fn, R.filter(isEven, items)));

filterEvensMap(R.negate, [1,2,3,4]); // [-2, -4]


However, since the above uses an anonymous function with the
fn
and
items
"glue parameters", I'm not sure this is the way that Ramda was intended to be used.

Below I included another way to do it. It seems to be more in the spirit of Ramda but I'm not sure if I'm over complicating things.

var filterEvensMap = R.compose(
R.flip,
R.uncurryN(2)
)(R.compose(
R.flip(R.map),
R.filter(isEven)
));


Am I overcomplicating with the multiple composes and uncurryN? Is there a more idiomatic way to achieve this? In your experience, does it matter?

Thanks in advance.

Answer

If you find Haskell signatures useful you may find this point-free generator (source) useful too. If you want to simplify an expression you can enter the Haskell equivalent to your JS code:

filterEvensMap = \fn items -> map fn (filter isEven items)

And it will give you a point-free equivalent:

filterEvensMap = (. filter isEven) . map

Then translate back to JS using Ramda:

var filterEvensMap = R.curry(R.compose(R.compose(R.filter(isEven)), R.map))

In your experience, does it matter?

I would go with the most readable expression, which in this case is probably the original expression. Point-free is fun and can add clarity in some places, but it can reduce readability considerably too, or at least level of understanding.

Comments