The problem is that the tutorial was transliterated from Haskell. So while it's useful for understanding the idea behind monads, the examples are not very motivating in a language like JavaScript. The one interesting example in the article for actual JavaScript use is probably the list monad which lets you compose functions returning an arbitrary number of results rather than just one (these functions are often called "nondeterministic").
However, it's possible to imagine other useful monads for JavaScript use. For example, I think we can abstract over CPS (continuation-passing style, like asynchronous functions in Node.js) functions using a monad.
This will involve changing our idea of CPS a little bit: instead of having a function f(x, k) where x is the argument and k is the callback, let's have a function f(x) which returns a function g(k) with x and k being the argument and callback respectively. Now we have to define the basic monad functions on it.
First, let's start with unit (which is called "return" in Haskell). The idea is simple: we want to go from some value to a function accepting a callback. What is a reasonable way to do this? Let's define it in a trivial way: we go from x to a function that, given a callback, immediately calls it with x. So:
CPS.unit = function (x) {
return function (k) {
k(x);
};
};
Next, we want to define bind. Now, what will bind do? The core idea is this: given a wrapped value m x and a function f: x → m y, we need to apply f to the wrapped value. Now, in this context, the "wrapped value" is a function accepting a callback. Essentially, this wrapped value is a promise to provide x at some point in the future. So we want to take this "promise" and get a new promise for the result of calling another CPS function on it. Here is a way to do this:
CPS.bind = function (f, x) { // f: x → m y, x: m x
return function (k) {
x(function (x_val) {
f(x_val)(k);
});
};
};
Admittedly, this code is a little confusing; I will try to explain it. The basic idea is simple: we create a new promise (the outermost returned function) that will trigger after x triggers and will then pass both the result of x (x_val) and the given callback (k) into f. That is, we get a promise of the result of f called with the eventual value of x.
This lets us bind together all these asynchronous values so that the result is still asynchronous. This makes managing "callback hell" simpler: instead of having a ton of nested callbacks, we can have a string of calls to bind. We also get a bunch of functions for free. For example, let's say you have a deferred x and want to apply a function to the result. This is essentially mapping the function over the deferred value. You could write it like this:
then make all your monad "types" (for lack of a better word) inherit from Monad. So the CPS type I showed here would get map for free, but so would an analogous List type.
Moreover, this is true for a whole bunch of useful functions. For example, you would be able to generalize reduce to accept a reducer function that behaves like a monad instead of like a normal function (you could call it reduceM). So you would then be able to reduce using an asynchronous function! I think that's pretty cool.
Note that it isn't enough just to write unit and bind to get a monad--the functions also have to follow some laws. If these laws aren't followed, you get all sorts of weird behavior with your monad. I think my CPS instance follows these laws, but I'm too lazy to check just this moment. I'll leave it as an exercise to the reader (I've always wanted to say that).
The reason we have to do some extra legwork (like creating the CPS object) is that JavaScript is not good at differentiating between types. Particularly, there is no way to consider only CPS functions. Additionally, there is a valid monad instance for all functions which behaves differently from the CPS one, and there would be no way to differentiate between the two without making it explicit. Now, I'm sure there is a neat way to make bind and unit methods of appropriate types, but that would make the examples even more complicated and I don't want to think about it right now :P.
In summary: monads let you generalize over a bunch of different concepts, not just IO. In particular, you can use monads to write code that works generically over CPS functions and nondeterministic functions and a bunch of others.
EDIT: I forgot to note: promises already have a well-known meaning in JavaScript. I don't actually know that meaning because I've never used them. The only reason I use the word is because I couldn't think of anything better to call them. It's likely the semantics of existing promises and the semantics of the objects I described are actually different, but they do serve the same purpose.
However, it's possible to imagine other useful monads for JavaScript use. For example, I think we can abstract over CPS (continuation-passing style, like asynchronous functions in Node.js) functions using a monad.
This will involve changing our idea of CPS a little bit: instead of having a function f(x, k) where x is the argument and k is the callback, let's have a function f(x) which returns a function g(k) with x and k being the argument and callback respectively. Now we have to define the basic monad functions on it.
First, let's start with unit (which is called "return" in Haskell). The idea is simple: we want to go from some value to a function accepting a callback. What is a reasonable way to do this? Let's define it in a trivial way: we go from x to a function that, given a callback, immediately calls it with x. So:
Next, we want to define bind. Now, what will bind do? The core idea is this: given a wrapped value m x and a function f: x → m y, we need to apply f to the wrapped value. Now, in this context, the "wrapped value" is a function accepting a callback. Essentially, this wrapped value is a promise to provide x at some point in the future. So we want to take this "promise" and get a new promise for the result of calling another CPS function on it. Here is a way to do this: Admittedly, this code is a little confusing; I will try to explain it. The basic idea is simple: we create a new promise (the outermost returned function) that will trigger after x triggers and will then pass both the result of x (x_val) and the given callback (k) into f. That is, we get a promise of the result of f called with the eventual value of x.This lets us bind together all these asynchronous values so that the result is still asynchronous. This makes managing "callback hell" simpler: instead of having a ton of nested callbacks, we can have a string of calls to bind. We also get a bunch of functions for free. For example, let's say you have a deferred x and want to apply a function to the result. This is essentially mapping the function over the deferred value. You could write it like this:
However, the true power of monads is that this map function works exactly the same way on every monad. So you could rewrite it like this: then make all your monad "types" (for lack of a better word) inherit from Monad. So the CPS type I showed here would get map for free, but so would an analogous List type.Moreover, this is true for a whole bunch of useful functions. For example, you would be able to generalize reduce to accept a reducer function that behaves like a monad instead of like a normal function (you could call it reduceM). So you would then be able to reduce using an asynchronous function! I think that's pretty cool.
Note that it isn't enough just to write unit and bind to get a monad--the functions also have to follow some laws. If these laws aren't followed, you get all sorts of weird behavior with your monad. I think my CPS instance follows these laws, but I'm too lazy to check just this moment. I'll leave it as an exercise to the reader (I've always wanted to say that).
The reason we have to do some extra legwork (like creating the CPS object) is that JavaScript is not good at differentiating between types. Particularly, there is no way to consider only CPS functions. Additionally, there is a valid monad instance for all functions which behaves differently from the CPS one, and there would be no way to differentiate between the two without making it explicit. Now, I'm sure there is a neat way to make bind and unit methods of appropriate types, but that would make the examples even more complicated and I don't want to think about it right now :P.
In summary: monads let you generalize over a bunch of different concepts, not just IO. In particular, you can use monads to write code that works generically over CPS functions and nondeterministic functions and a bunch of others.
EDIT: I forgot to note: promises already have a well-known meaning in JavaScript. I don't actually know that meaning because I've never used them. The only reason I use the word is because I couldn't think of anything better to call them. It's likely the semantics of existing promises and the semantics of the objects I described are actually different, but they do serve the same purpose.