Iven Marquardt

Posted on

# Another Flaw of the Promise Type: Intertwining of in-sequence and in-parallel

From a functional perspective `Promise` is a poorly designed data type, because it is lawless, an unprincipled abstraction, rather belonging to the quirky part of Javascript.

In this brief post I will demonstrate another flaw of the `Promise` type: It intermingles asynchronous computations that have an in-sequence semantics with those that have an in-parallel one.

Why should both forms be distinguished? Because...

• async computations in-parallel are not monads (in-sequence are)
• both result in different algebraic structures

The former statement is clear, provided you know what a monad is. However, the ladder is a bit harder. Both forms of async computations are just very different and thus their approaches to handle different scenarios vary. Let's compare their monoids to illustrate this statement.

### `Task` - async in sequence

`Task` sequentially performs asynchronous computations. It is a monad but also a monoid:

``````// Task type

thisify(o => {
return res(x);
}, rej);

return o;
}));

const tEmpty = empty =>
() => Task((res, rej) => res(empty()));

const tAppend = append => tx => ty =>
res(append(x) (y)), rej), rej));

const sumAppend = x => y => x + y;
const sumEmpty = () => 0;

// some async functions

const delayTask = f => ms => x =>
Task((res, rej) => setTimeout(comp(res) (f), ms, x));

const tInc = delayTask(x => x + 1) (10); // 10ms delay
const tSqr = delayTask(x => x * x) (100); // 100ms delay

// MAIN

const main = tAppend(sumAppend) (tSqr(5)) (tInc(5));
//                   ^^^^^^^^^ monoid of the base type

``````

run code

Do you see how succinct this `Task` implementation is compared to a Promise/A+ compliant one?

The monoid takes a monoid from a base type and lifts it into the context of asynchronous computations in sequence, that is, `tAppend` takes a monoid from another type and applies it as soon as both async operations have yielded a result. Don't worry if this is too abstract. We will have an example soon.

### `Parallel` - async in parallel

`Parallel` performa asynchronous computations in parallel. It is only an applicative and monoid but not a monad:

``````// Parallel type

const Parallel = para => record(
Parallel,
thisify(o => {
o.para = (res, rej) =>
para(x => {
o.para = k => k(x);
return res(x);
}, rej);

return o;
}));

// Parallel monoid

const pEmpty = () => Parallel((res, rej) => null);

const pAppend = tx => ty => {
const guard = (res, rej) => [
x => (
isRes || isRej
? false
: (isRes = true, res(x))),
e =>
isRes || isRej
? false
: (isRej = true, rej(e))];

let isRes = false,
isRej = false;

return Parallel(
(res, rej) => {
tx.para(...guard(res, rej));
ty.para(...guard(res, rej))
})
};

// some async functions

const delayPara = f => ms => x =>
Parallel((res, rej) => setTimeout(comp(res) (f), ms, x));

const pInc = delayPara(x => x + 1) (10); // 10ms delay
const pSqr = delayPara(x => x * x) (100); // 100ms delay

// MAIN

const main = pAppend(pSqr(5)) (pInc(5));

main.para(console.log); // logs 6
``````

run code

`Parallel`'s monoid instance represents the race monoid, i.e. `pAppend` picks the result value of the faster one of two asynchronous computations.

### Conclusion

Both monoids are completely different, because `Task` and `Parallel` are different notions of asynchronous computations. Separating them is laborious at first but leads to more declarative, more predictable and more reliable code. There is a transformation between `Task` and `Parallel` and vice versa, so you can easily switch between both representations.

Read more on functional programming in Javascript in my online course.