Another Flaw of the Promise Type: Intertwining of in-sequence and in-parallel
Tags: functional, learning, javascript
Published: 2020-05-18 15:52:56

---
title: Another Flaw of the Promise Type: Intertwining of in-sequence and in-parallel
published: true
description: 
tags: #functional #learning #javascript
---

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:

```javascript
// 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
```
[run code](https://repl.it/repls/FatalVibrantGlitch)

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:

```javascript
// 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](https://repl.it/repls/DrabKaleidoscopicCosmos)

`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](https://github.com/kongware/scriptum/blob/master/README.md).