0

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.

Lopatin
  • 13
  • 2

1 Answers1

2

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.

tex
  • 2,756
  • 22
  • 31
elclanrs
  • 92,861
  • 21
  • 134
  • 171
  • Hey thanks for the tips, pointfree.js looks really helpful. There's a slight mismatch though, between the intended behavior in the OP and the Ramda expression derived from the point-free Haskell expression: The filtering needs to happen on the original vals, not the mapped vals. Here's a link that shows the difference: http://goo.gl/KVX4Kv. I'm not that familiar with Haskell syntax so I'm still trying to find where the issue is. – Lopatin Feb 17 '16 at 04:45
  • Oh, I made a mistake, it should be `compose(pipe(filter(f)), map)` but it wont work in Ramda for some reason. My guess is that the auto-currying and variadic compose/pipe mess things up, but you can see it working in vanilla JS here: https://jsfiddle.net/xs56h0vb/ – elclanrs Feb 17 '16 at 18:56
  • Thanks again! I'm marking as answered because I have everything that I need to continue programming. I'm now aware that what I was referring to in the question is a concept called "pointfree" and that it's not always necessary it to write a pointfree expression just because it's possible. But when I want to for fun, there is pointfree.js – Lopatin Feb 17 '16 at 22:16
  • Just submitted an edit to wrap your example Ramda code in `R.curry` (functions created with `R.compose` don't curry by default). There's also a slight refactor available for the original version, too `var fmap = R.uncurryN(2, f => R.compose(R.map(f), R.filter(isEven)))` to remove the extra `items` argument. – snickle Jul 26 '16 at 15:42