proposal-pipeline-operator
proposal-pipeline-operator copied to clipboard
pipeline-equals assignment
In the proposal I saw no mention of something like variable |>= fn
as a clean shorthand for variable = fn(variable)
, so I thought I'd bring it up so it can be added to the spec with this proposal, and doesn't bite us later.
You might want to discourage it as JS is going more and more functional, but if so, it would be nice to have that written in the proposal as well
I think that this could be useful for cases where you want to pass properties through the pipeline. This would solve a similar problem to async
/await
by allowing you to use operator results in the same scope as other operators in the pipeline.
A Promises example:
I think it's easier to exemplify with a working Promises example. This is a real world scenario: You need to read some items from storage, set some configuration, and make a network request -- all async operations.
getItemsAsync().then(items =>
setConfigAsync(items).then(
() => httpGet(items),
),
);
This creates more rightward drift in the code. Alternatively you could use let
:
let oitems;
getItemsAsync().then(items => {
oitems = items;
return setConfigAsync(items);
}).then(() => httpGet(oitems));
async
/await
solves this problem by putting all of the operators in the same scope. Imagine we're in an async function...
const items = await getItemsAsync();
await setConfigAsync(items);
return httpGet(items);
I think this is much easier to reason about, and it doesn't require "carrying values around," if that makes sense.
Applying to the pipeline
Imagine an Observable chain that does the same operations:
getItems$().pipe(
mergeMap(items => setConfig$(items).pipe(
mergeMap(() => httpGet$(items)),
),
);
... alternatively ...
getItems$().pipe(
mergeMap(items => setConfig$(items), (_, items) => items),
mergeMap(httpGet$),
);
If the chain gets longer, the carrying around of items becomes more cumbersome.
You can see complaints about this here: https://github.com/ReactiveX/RxJava/issues/2931 and quite a few other places.
Solution using the pipeline and assignment
If you can do assignment in the pipeline this problem is solved by keeping the values in the same scope as the other operators:
// `of` is used here to kick off the Observable chain
of(1)
const items |>= mergeMap(() => getItems$()),
|> mergeMap(() => setConfig$(items)),
|> mergeMap(() => httpGet$(items)),
This syntax isn't perfect, but I think the general idea is good.
I'd like to leave this idea for a follow-on proposal, and leave the initial proposal simpler and minimal. What do you think?
@ajcrites Hey, I see your reasoning for wanting this though for your scenario
getItemsAsync().then(items =>
setConfigAsync(items).then(
() => httpGet(items),
),
);
which is the same as:
getItemsAsync()
.then(setConfigAsync)
.then(httpGet);
The composition, in my opinion, would be better composed as 'something that happens outside of an observable' which can then be called from an 'observable':
const someProcess = async () =>
await getItemsAsync()
|> x => (await setConfigAsync(x), x) // I know, just for our example
|> x => await httpGet(x)
;
no (in other words, you wouldn't even need observable chains involved, for process, in javascript)?
// Updated example above
Same as example above but without sequences:
const
someProcess =
async () => getItemsAsync()
.then(x => setConfigAsync(x).then(() => httpGet(x))
;
@ajcrites @towc 👍 for participation/effort 👍 Also @ajcrites The someProcess
I show above is 'point-free' and allows the process to be easily composed with other function calls of a simliar structure (async functions etc.). Also no mutability (in example) 👍