Hello Rubens, cool article! Functional programming and category theory in JavaScript is so much fun!

That Symbol.species tricks is extremly cool by the way, I’ll keep it for later.

However, I’m not sure if I understandd correctly your explanation about monads (the quote from Mr Eliott about the characteristics of a monad are not very clear to me).

I get that a monad is a functor (it has a `map`

method) with a `flatMap`

function and a `of`

function that let you wrap a value with monad.

What bugs me in your Zord monad is that the flatMap function can unwrap/flatten of as many levels as needed in order to have access to the wrapped primitive value.

From what I read here (http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html) and here (https://en.wikibooks.org/wiki/Haskell/Category_theory#Monads), I don’t think your Zord flatMap method respect the monad flatMap signature i.e :

(flatMap/chain/bind) :: Monad a -> (a -> Monad b) -> Monad b

flatMap expects a function `(a -> Monad b)`

that takes a value (the value wrapped inside the monad) and returns a value wrapped in the said monad.

In the case of Zord, I would have been :

// Let's wrap a color inside a Zord

const z1 = Zord.of('blue')

const z2 = z1.flatMap(color => {

console.log(color) // -> 'blue'

const newColor = addColors(color, 'red')

console.log(newColor) // -> 'purple'

return Zord.of(newColor)

})

// z2 is a new Zord instance wrapping 'purple'

But if we have more levels of nesting :

const z1 = Zord.of(Zord.of(Zord.of('blue')))

console.log(z1) // -> Zord(Zord(Zord('blue')))

const z2 = z1.flatMap(value => {

console.log(value) // -> 'Zord(Zord(blue))'

return value

})

console.log(z2) // -> Zord(Zord('blue'))

Here, we are under the impression that the data structure has been flattened of one level, but this behavior is caused by the actual definition of flatMap. This case is explicitely the case where we pass the identity function `x => x`

to flatMap.

In fact according, to the monad definition here, `flatMap(x => x)`

is equivalent to the `join`

function that flattens one level of nesting i.e.

join :: Monad (Monad a) -> Monad a

const z = Zord.of(Zord.of(Zord.of('blue')))

z.join() // Zord(Zord('blue'))

z.flatMap(x => x) // Zord(Zord('blue'))

z.join().join() // Zord('blue')

z.flatMap(x => x).flatMap(x => x) // Zord('blue')

If I reuse the code snippet from your article, I think that we should get :

Zord.of('white')

.flatMap(rng => console.log(rng)); // logswhite

Zord.of(Zord.of('white'))

.flatMap(rng => console.log(rng)); // logs Zord(white)<--- ?

In the case of Promises, there is an exception because `Promise.resolve`

does not behaves like the `Monad.of`

function of the definition of the monad, it flattens promises automagically :) and `.then`

conflates the behaviors of `.map`

and `.flatMap`

.

Promise.resolve('hello')

.then(res => console.log(res))

// -> 'hello'

Promise.resolve(Promise.resolve(Promise.resolve('hello')))

.then(res => console.log(res))

// -> 'hello' and not Promise(Promise('hello'))

So I think Promise can be called a ‘special’ monad in JavaScript :)

// .then behaves like .map

Promise.resolve(42)

.then(x => x + 1)

.then(x => console.log(x)) --> 43

// .then behaves like .flatMap

Promise.resolve(42)

.then(x => Promise.resolve(x + 1))

.then(x => console.log(x)) --> 43

Phew! What a wall of text …

Tell me what you think about this!

I’m far from being a category theory specialist but I’m still learning and I’m eager to understand/master these concepts! :)