ctcutler
ctcutler

Reputation: 43

How can I compose functions when the input is needed to build the pipeline?

I'm attempting to learn some functional programming in javascript using the Ramda library and I keep running into situations where I compose a bunch of functions and then have to pass the input to the result twice. The following, working code is an example:

const search = R.invoker(1, 'search');
const substring = R.invoker(2, 'substring');
const lSplit = (pat, s) => R.compose(substring(0), search(pat))(s)(s);
lSplit(/ /, 'foo bar');

lSplit takes a regex and a string, locates the first match in the string, and returns everything to the left of the match. The last line evaluates to 'foo'. It is necessary to pass s to the composed function twice: first to find the pattern and build the substring function and then a second time to run the composed function on the string. The code works, but the (s)(s) seems inelegant.

I wrote a function called twice that tidies things up a bit but doesn't really address the inelegance:

const search = R.invoker(1, 'search');
const substring = R.invoker(2, 'substring');
const twice = f => d => f(d)(d);
const lSplit = pat => twice(R.compose(substring(0), search(pat)));

lSplit(/ /)('foo bar');

Is there a cleaner or more elegant way to handle situations where you need to use your data to build your pipeline before feeding it into the pipeline?

Upvotes: 4

Views: 169

Answers (1)

Mulan
Mulan

Reputation: 135367

The reason your composition seems messy is because you're composing a binary function search with a ternary function substring. To make matters worse, the "piped" argument takes place in the middle of the ternary function

               _ search(/ /, s)
              /
substring(0, /, s)

Instead of worrying about being point free, I would just write the function that expresses it best

const lSplit = (pat, s) => substring(0, search(pat,s), s);

It's not clever, but it's readable. My recommendation is to let done be done.

Upvotes: 3

Related Questions