3

I have two curried functions f and g:

f: a -> b -> c
g: a -> c -> d

I want to create h:

h: a -> b -> d

Currently I'm composing them via pipe:

const h = a => pipe(f(a), g(a));

Is there a way to do this point free?

The reason why I ask is because I want to make the code as readable as possible. In some of my cases there are more that two functions that receive initial arguments in the composition and I'm trying to avoid repeatedly passing arguments.

I tried something like:

const h = converge(pipe, [f, g]);

which for some reason doesn't work. I guess I don't understand converge correctly.

Edit:

I tried what you suggested Ori Drori, but it doesn't work.

const modulo = divisor => dividend => dividend % divisor;

const maybeReduceByModulo = modulo => number =>
  number >= 0 ? number : number + modulo;

export const maybeReducedModulo = flip(ap)(modulo, maybeReduceByModulo);
/**
 * Before:
 * export const maybeReducedModulo = divisor =>
 *   pipe(modulo(divisor), maybeReduceByModulo(divisor));
 */
8
  • Return one function through the other Commented Sep 29, 2021 at 12:41
  • @AlphaHowl what do you mean? Commented Sep 29, 2021 at 12:42
  • Never mind I didn't read the question well Commented Sep 29, 2021 at 12:49
  • My solution doesn't work. Back to the drawing board. Commented Sep 29, 2021 at 17:14
  • 1
    @customcommander: it does work, as I understand the question. See the end of my answer. But I think this is less readable than a vanilla JS version, and it still doesn't manage to get all the way to point-free. Commented Sep 29, 2021 at 18:19

1 Answer 1

3

Ramda (disclaimer: I'm a Ramda author) cannot directly offer every combinator one might need. It does contain a number of them. But for those it doesn't contain, it's often trivial to write your own version. For me the big problem is in naming. It's hard to come up with names for them, and even those who resort to bird names can only give names for a few of the infinity of possibilities.

I can't find this one in Avaq's handy list of combinators, nor can I find :: (a -> b -> c) -> (a -> c -> d) -> a -> b -> d or :: (a -> b -> c) -> (a -> c -> d) -> (a -> b -> d) on Hoogle, which makes me suspect that this is a fairly uncommon requirement. But if it's a requirement you have, invent your own name.

Once you've chosen a name, such combinators often write themselves.

const foo = (f) => (g) => (x) => (y) => g (x) (f (x) (y))

const f = (a) => (b) => `f (${a}, ${b})`
const g = (a) => (c) => `g (${a}, ${c})`
const h = foo (f) (g)

console .log (h ('a') ('b'))

And of course you can play with the signatures in various ways. Ramda's curry might help here. Perhaps we want this:

const foo = (f, g) => (x) => (y) => g (x) (f (x) (y))
const h = foo (f, g)

or even further, this:

const foo = curry ((f, g) => curry ((x, y) => g (x) (f (x) (y))))
const h = foo (f, g)  // equivalent: `foo (f) (g)`
h ('a', 'b')          // equivalent: `h ('a') ('b')`

The suggestion from customcommander does work:

const f = (a) => (b) => `f (${a}, ${b})`
const g = (a) => (c) => `g (${a}, ${c})`
const h = compose (apply (pipe), ap ([f, g]), of)

console .log (h ('a') ('b'))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script> const {compose, apply, pipe, ap, of} = R </script>

But I think it's not nearly as understandable as the more explicit vanilla JS version.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.