Fluture icon indicating copy to clipboard operation
Fluture copied to clipboard

Parallel transformations are run twice when one of the Futures resolves the other synchronously

Open Avaq opened this issue 4 years ago • 4 comments

First discovered in https://github.com/fluture-js/fluture-hooks/pull/6, this is one of several thinkable issues that arise once control over the resolution of a Future is taken outside of the Future constructor, for example:

let exportedRes;

// We're defeating Fluture's design and exfiltrating the res function:
const m1 = Future ((rej, res) => {
  exportedRes = res;
});

// Then we create another Future which forces the resolution of the former:
const m2 = Future ((rej, res) => {
  res (42);
  exportedRes (42);
});

// Now when we (re)combine these two Futures with Fluture, Fluture
// is going to run the effect of `both` (or whatever combinator was
// used) twice: once as a result of m2 starting a parallel process,
// and once more for m1 resolving as a result of that parallel process.
both (m1) (m2)

The example above is very similar to what fluture-hooks does to achieve its parallel applicative instance. Arguably, this is not a bug, because the user is abusing Fluture: they're manually combining m1 and m2 outside of Fluture's API for combining Futures. However, since it's possible to achieve this at all; I still want to record it as a bug and think about how Fluture should behave under these scenarios.

Avaq avatar Sep 07 '19 21:09 Avaq

It's might also be worth addressing why a user would want to exfiltrate the res function in the first place, and work towards allowing the user to substitute it with a more idiomatic pattern.

In the case of fluture-hooks, it is required because we need a mechanism for two Futures to hang until some external process has finished its thing, at which point both Futures should resolve.

Avaq avatar Sep 07 '19 21:09 Avaq

Will this issue be resolved, if rej and res functions are wrapped in a way that makes them idempotent?

safareli avatar Jul 06 '21 11:07 safareli

Hi @safareli :)

No. Rej and res are already wrapped for idempotence:

https://github.com/fluture-js/Fluture/blob/f349eb4555871484828d796d9c00d63aff7209f9/src/future.js#L153-L169

The problem happens because the internal mechanics of both (or any parallel combinator), and how it combines the two parallel Futures.

Avaq avatar Jul 06 '21 16:07 Avaq

It's built on the assumption that the userland of one Future doesn't trigger signals in the other. So the signals from the other future are not guarded while running the one Future.

Avaq avatar Jul 06 '21 16:07 Avaq