user2410339
user2410339

Reputation:

Can this function be rewritten as a Ramda pipe? How?

I want to know if it is possible to rewrite A as a Ramda pipe where D waits on C without passing the result of C into D:

const A = async payload => {
    const resultB = await B(payload);
    const resultC = await C(resultB);
    const resultD = await D(resultB);
    return resultB;
};

Edit: this doesn't seem to be producing desired results:

const R = require('ramda');

const then = R.curry((f, p) => p.then(f));

const trace = R.curry(async(name, data) => `${name}(${data})`);

const B = trace('B');
const C = trace('C');
const D = trace('D');

const A = async payload => {
    const resultB = await B(payload);
    await C(resultB);
    await D(resultB);
    return resultB;
   };

const A_Pipe = R.pipe (B, then(C), then(D));

A('x').then(console.log); // -> B(x)

A_Pipe('x').then(console.log); // -> D(C(B(x)))

Upvotes: 1

Views: 679

Answers (2)

richytong
richytong

Reputation: 2462

Not ramda, but it does what you want.

const { pipe, tap } = require('rubico')

const A = pipe([
  B,
  tap(C),
  tap(D),
])

Upvotes: 0

Mulan
Mulan

Reputation: 135367

Apparently Ramda plans to add R.then but it looks like they haven't gotten around to it yet

Until then, you can make your own

const then = 
  R.curry((f, p) => p.then(f))

const A =
  R.pipe(B, then(C), then(D))

Here's a complete program you can paste in the Ramda REPL

const then = f => p =>
  p.then (f)

const effect = f => x =>
  (f (x), x)

const trace =
  effect (console.log)

const fakeFetch = x =>
  new Promise (r => setTimeout (r, 200, x))

const B = x =>
  fakeFetch (trace (`[B: ${x}]`))

const C = x =>
  fakeFetch (trace (`[C: ${x}]`))

const D = x =>
  fakeFetch (trace (`[D: ${x}]`))

const A =
  pipe (B, then (C), then (D))

A (1)
// => { Promise "[D: [C: [B: 1]]]" }

Output

[B: 1]
[C: [B: 1]]
[D: [C: [B: 1]]]

I see what you did there

Upon closer inspection, C and D are side-effecting functions whose return value is discarded – resultC and resultD are not used. Rather, resultB is the only value you seem to care about

const A = async payload => {
  const resultB = await B(payload)
  const resultC = await C(resultB)
  const resultD = await D(resultB)
  return resultB
}

When composing functions with R.compose or R.pipe, the return value of one is passed to the next. In your case however, C and D should not impact the input. I introduce asyncTap to encode your intention – compare to R.tap or effect above

const asyncTap = f => p =>
  p.then (R.tap (f))

const A =
  pipe (B, asyncTap (C), asyncTap (D))

A (1) .then (console.log, console.error)
// => { Promise "[B: 1]" }

Output – see complete program in the Ramda REPL

[B: 1]
[C: [B: 1]]
[D: [B: 1]]
[B: 1]

This begs the question: what are you doing with resultC and resultD? Functional programming is about writing programs with pure, side-effect-free functions. When you're having difficulty expressing your program in a functional way, it can sometimes indicate you're not thinking in a functional way.


Upvotes: 4

Related Questions