Hacking JS async/await to chain Monads
Tags: monad, functional, javascript, showdev
Published: 2019-07-23 21:55:57

# The M word.
```hs
then :: Monad m => m a ~> (a -> m b) -> m b
```

# `await` a minute
Native JS promises follow the [https://promisesaplus.com/](A+ promise spec). A+ compliant promises implementation can interoperate, but it does so by being hopeful that anything that implements a `then` method will behave like a promise, and that's the point we'll be hacking in.

From the spec, the important parts for us are:

> 1.2  “thenable” is an object or function that defines a then method.

[...]

> 2.2  A promise’s then method accepts two arguments:
> `promise.then(onFulfilled, onRejected)`

[...]

> 2.2.2  If `onFulfilled` is a function:
>   2.2.2.1  it must be called after promise is fulfilled, with promise’s value as its first argument.
>   2.2.2.2  it must not be called before promise is fulfilled.
>   2.2.2.3  it must not be called more than once.

And the most significant bit:

> [...] If `x` is a `thenable`, attempts to make promise adopt the state of `x`, under the assumption that `x` behaves at least somewhat like a promise. Otherwise, it fulfills promise with the value `x`.

All of that means that:
1 - We must implement a `then` method, to trick the `The Promise Resolution Procedure` into calling it. It will be a alias for the `bind` operation.
2 - As by `2.2.2.3`, our `then` will be feed a `onFulfilled` function that expects only one call, i.e. chaining enumerations won't be possible.

# Tricking JS
Consider the following monad:
```javascript
const Const = (x) => ({
  then (onFulfilled) {
    return onFulfilled(x)
  }
})

const distopy = Const(1000)
  .then(x => Const(x + 900))
  .then(x => Const(x + 80))
  .then(x => Const(x + 4)) // Const(1984)

```
`then`'s signature is: `then :: Const a ~> (a -> Const b) -> Const b`

Now, I want a function that given to `Const number`, returns a `Const`* with the sum of both. I just need to write something like:

```javascript
function sumConsts (constA, constB) {
  return constA
    .then(a => constB
      .then(b => Const(a + b)
    )
  )
}
```

The more `Const`s we need to sum, the more it will look-like a callback-hell, so I'd take the advantage of `Const` being a `thenable` and refactor `sumConsts` as:
```javascript
const sumConsts = async (constA, constB) => Const(await constA + await constB)
```

But now, as `async function`s always returns a promise into the returned value and `Const` is a `thenable` the promise resolution procedure will kick in, and make the returned promise "attempts to adopt the state of" it, so will never get a `Const` back, but as both `Const` and promises implement the same interface, the `Const` semantic is kept.

# `Maybe` another example
```javascript
const Maybe = {
 Just: (v) => {
   const typeofV = typeof v
   if (typeofV === 'undefined' || typeofV === 'null') {
     return Maybe.Nothing
   }

   return {
     then (onFulfilled) {
       return onFulfilled(v)
     }
   }
 },

 Nothing: {
   // You can either never call `onFulfilled`, so a Nothing never resolves.
   // then() {},

   // Or call `onRejected`, so resolving a Nothing rejects the promise
   then(onFulfilled, onRejected) {
     onRejected(Maybe.Nothing)
     return Maybe.Nothing
   }
 }
}

function flipCoin (myGuess) {
  const coin = Math.random() < 0.5 ? 'heads' : 'tails'
  if (coin === myGuess) {
    return Maybe.Just (myGuess)
  } else {
    return Maybe.Nothing
  }
}

async function playIt (guess = 'heads', tries = 1) {
  try {
    await flipCoin (guess)
    return tries
  } catch (reason) {
    if (reason === Maybe.Nothing)
      return playIt(guess, tries + 1)
    else
      throw reason
  }
}

playIt()
  .then(console.log) // avg output: 2
```