Discovering the notion of Monad in Javascript
Published: 2023-10-30 12:52:58

You already know what Monads are about, so I won't repeat what you can read about them in the numerous articles on the net.

I am taking the point of vue of someone completely novice who wants to apprehend this notion without any mathematical concepts.

For me, all starts with the notion of **closure**.

_A closure is a feature of JavaScript that allows inner functions to access the outer scope of a function._

Ok, let's write our first closure :

```js
const adder = (m) => (n) => m + n;
const add2 = adder(2);
console.log(add2(5)); // 7
```
Yes, it's a little magic, but that is the way it works.

So, the question is what if we want to generalise the function to apply to a given value (here 5) ?

In order to simplify things, the function to apply will take a single argument.

So we could write :

```js
const addTwo = x => x + 2
const transform = (value) => (fn) => fn(value)
console.log(transform(5)(addTwo)) //7
```
With that in place, we can pass whatever function to our transform
function, nice.

Now, let the expression **transform(5)(addTwo)** be the result of a function called **PeudoMonad** and let's rename **transform** by **map** and switch the arguments, we get then :

```js
const PseudoMonad = (value) => ({
   map (fn) {
    return fn(value)
  }
})
console.log(PseudoMonad(5).map(add2)) // 7
```

What if we could, carry the context of our **PseudoMonad**, ie carry the initial value ?

Let's rewrite our PseudoMonad :

```js
const PseudoMonad = (value) => ({
   value,
   map (fn) {
    return fn(this.value)
  }
})
console.log(PseudoMonad(5)) // { value: 5, map: [Function: map] }
```
Great, along our computation, with get the initial value, which is unchanged

So what do we get so far, an object which remember of it's initial state and a method which operates transformations on this state.

A question arise, how can we chain these transformations ?
Actually the return type of our **map** is a number.

Instead of returning a value of type number from our **map** function, what if we return an object of type **PseudoMonad** 

Let's try :

```js
const PseudoMonad = (value) => ({
   value,
   map (fn) {
    return PseudoMonad(fn(this.value))
  }
})
```
The **map** method returns now an object of type **PseudoMonad**

Let's see if that works :

```js
const result =  PseudoMonad(5)
    .map(x => x + 3)
    .map(x => x * 100) // 7

console.log(result)
```

Result : 

```js
{ value: 800, map: [Function: map] }
```
Great, we can easily chain transformations to our PeudoMonad.

If you wonder what is the deal with this, look at the following
example :

```js


function validUser (user) {
  return user.age >= 18
}

const PseudoMonad = (value) => ({
   value,
   map (fn) {
    if (!validUser(this.value)) {
       return PseudoMonad(null)
    }
    else {
       return PseudoMonad(fn(this.value))
    }
  }
});

const Hal = PseudoMonad({
  name : "Hal",
  age : 3,
  numbadge : -1
});

const John = PseudoMonad({
  name : "John",
  age : 34,
  numbadge : -1
});

const Alice = PseudoMonad({
  name : "Alice",
  age : 24,
  numbadge : -1
});

function getBadge() {
  let  numberbadge = 0
  function _getBadge (user) {
    numberbadge += 1
    user.numbadge = numberbadge
    return user
  }
  return _getBadge
}


const attendees = [Hal, John, Alice]
  .map(u => u.map(getBadge()))
  .filter(u => u.value)

console.log(attendees.map(v => v.value.name))

```

The getBadge function is always passed a valid user, and the same for the eventually chained functions.

**Hal** is not passed to **getBadge**. (getBadge() here)

The callers don't have to do any checkings and so, they are easlily testables, that's the big deal.

Betwise, the PseudoMonad described here, is an approximation of what is a called a Maybe monad.

[Demo](https://flems.io/#0=N4IgZglgNgpgziAXAbVAOwIYFsZJAOgAsAXLKEAGhAGMB7NYmBvEAXwvW10QICsEqdBk2J4AOmjABXNNWIR6AAgBuGKBAAmAVTgwATooAUU3XoCUi4BMWK9MYlL1pFJ-fgwBzGIoB8AXkUARgAOCVYJCSE4YkUABV0pDVoAWXoMDUUAw1UoKRgLPx8jK2cbHLyKaxssDAAHIzA0CxKbGwgwIwBCHM0dfUNiQgg4fHL85qrWmzsHJziEpNTMDUM0KSgoM0nFcNKbGChdS23WmcdneJhElLSVxoGhkbGzLb2dqt3WMwBuCNl6aKKAASaky8yui1uhhaikwOEUiEUYhAIKgyMqpU83kRAGYMTY1lgAEbpLwIxQAWkCYR+fyiMQAUrRCM4Apdrkt0tCqnDsUiQEyWeiqljyTiACz42FSYmkvlUmm-NCRAExACC6mo3jZCxuy25pV55ORGogWuFmLJiIATJKeTKSRorZTqWgvkqJNJZPIlF5iAAhOWGCalWAxaXE-SOskBAAMVS9cgUzgA+n7A07vMZTCGplFaLB8FBaB5s25ea8poSiVG5YoANQBV1TVx6fDVusBau1zPbM5zVsfKr91PpuVhP4qtCAjDERhoDQweBg5CoiiKQVodemrUAXSq+BqtWMmSKUkPdUMY8zwZeB8gUEYehPhRcozUeVeU7gBZgRZLhizvOi7wBex7KKeKjvrkf4Vl+yoIX8lAgLosBJgCeA4sEiDWgAbBSWGILGbAcCAvJ4Pg1BwAIND0POog8Gwu6sEAA)