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
const Task = task => record(
Task,
thisify(o => {
o.task = (res, rej) =>
task(x => {
o.task = k => k(x);
return res(x);
}, rej);
return o;
}));
// Task monoid
const tEmpty = empty =>
() => Task((res, rej) => res(empty()));
const tAppend = append => tx => ty =>
Task((res, rej) =>
tx.task(x =>
ty.task(y =>
res(append(x) (y)), rej), rej));
// Number monoid under addition
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
main.task(console.log); // logs 31
```

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
```

`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.

## Top comments (0)