Reputation:
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
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
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