A simple introduction to why Monads are useful
If you're a seasoned developer, chances are you have already used monads in your daily practice without even realizing it. For instance, a jQuery Deferred object is a monad. So is jQuery Ajax, as well as Bacon.js EventStream. So this shouldn't be too hard to follow. On occasion, I will reference similarities between the JavaScript example and its counterparts in the programming language Haskell. I do this only because most formal literature on monads references Haskell, and it helps to become familiar with the language. Feel free to skip these parts if you prefer. Maybe We Have a Problem After the Identity monad, the Maybe monad is perhaps one of the simplest examples of a monad available. It's also quite useful. The Maybe monad provides an elegant solution to the common problem of multiple nested null checks in code. Consider the following trivial example: var person = { }, isNothing: function() { }, val: function() { }; }; if (typeof value === 'undefined' || value === null) return Nothing; return Something(value); }; Now we have a function Maybe (in monadic terms, our unit function) that returns an object Nothing if the value provided to it is null or undefined and returns a function Something that returns the original value if the value is not null or undefined. (For clarity, I've renamed Haskell's Just with Something, as I find this terminology a bit easier to follow when first being introduced to the concept.) What can we do with the above? Let's try it out: Maybe(null) == Nothing; // true typeof Maybe(null); // 'object' Maybe('foo') == Nothing; // false Maybe('foo')(); // 'foo' typeof Maybe('foo'); // 'function' So now we've put all of our != null checks into a single function, which is the constructor for the new Maybe type. But is this enough to solve our problem? Unfortunately, no. Let's try it out: if (Maybe(person) != Nothing && Maybe(person["address"]) != Nothing) { var state = Maybe(person["address"]["state"]); if (state != Nothing) { = m Thus, an additive monad is also a monoid. For >>=, on the other hand, m0 acts as a null-element. Just as multiplying a number by 0 results in 0, binding m0 with any function produces the zero for the result type: m0 >>= f return { return { return acts approximately as a neutral element of >>=, in that: throw new Error("cannot call val() nothing"); let! x = readNum() let! y = readNum() let! x = readNum() let! x = readNum() if (y = 0) console.log(state); } else { "name":"Homer Simpson", return def; } }; }; if (typeof value === 'undefined' || value === null) return true; return >=> g = g f >=> return = f (f >=> g) >=> h = f >=> (g >=> h) fmap and join Although Haskell defines monads in terms of the return and bind functions, it is also possible to define a monad in terms of return and two other operations, join and fmap. This formulation fits more closely with the original definition of monads in category theory. The fmap operation, with type (t°u ) ° M t°M u,[12] takes a function between two types and produces a function that does the "same thing" to values in the monad. The join operation, with type M (M t)°M t, "flattens" two layers of monadic information into one. The two formulations are related as follows: fmap f m = m >>= (return . f) join n = n >>= id m >>= g = a + (b + c) m + m0 console.log(state); } else { return >=> g = g f >=> return = f (f >=> g) >=> h = f >=> (g >=> h) fmap and join Although Haskell defines monads in terms of the return and bind functions, it is also possible to define a monad in terms of return and two other operations, join and fmap. This formulation fits more closely with the original definition of monads in category theory. The fmap operation, with type (t°u ) ° M t°M u,[12] takes a function between two types and produces a function that does the "same thing" to values in the monad. The join operation, with type M (M t)°M t, "flattens" two layers of monadic information into one. The two formulations are related as follows: fmap f m = m >>= (return . f) join n = n >>= id m >>= g = a + (b + c) m + m0 The unary return operation takes a value from a plain type and puts it into a monadic container of type M a; the bind operation chains a monadic value of type M a can be thought of as an action that returns a value of type IO a represents an action that, when performed, produces a value of type M a can be thought of as an action that takes as its argument the current state of the world, and will return a new world where the state has been changed according to the function's return value. For example, the functions doesFileExist and removeFile in the standard Haskell library have the following types doesFileExist :: FilePath -> IO () So, one can think of removeFile as a function that, given a FilePath, returns an IO action; this action will ensure that the world, in this case the underlying file system, won't have a file named by that FilePath when it gets executed. Here, the IO internal value is of type () which means that the caller does not care about any other outcomes. On the other hand, in doesFileExist, the function returns an IO action which wraps a boolean value, True or False; this conceptually represents a new state of the world where the caller knows for certain whether that FilePath is present in the file system or not at the time of the action is performed. The state of the world managed in this way can be passed from action to action, thus defining a series of actions which will be applied in order as steps of state changes. This process is similar to how a temporal logic represents the passage of time using only declarative propositions. The following example clarifies in detail how this chaining of actions occurs in a program, again using Haskell. We would like to be able to perform some basic operations with I/O actions: We should be able to sequence two I/O operations together. In Haskell, this is written as an infix operator >>, so that putStrLn "abc" >> putStrLn "def" is an I/O action that prints two lines of text to the console. The type of >> is IO a ° IO b ° IO b, meaning that the operator takes two I/O operations and returns a third that sequences the two together and returns the value of the second. We should have an I/O action which does nothing. That is, it returns a value but has no side effects. In Haskell, this action constructor is called return; it has type a ° IO a. The bind operator unwraps the plain value a embedded in its input monadic value M a, and feeds it to the function. Have a unit function that converts a value of some type to its corresponding monadic type. This is the Maybe function. return (op x y) Recall that arrows in a type associate to the right, so liftM2 is a function that can pick an I/O action based on the value produced by the action on the left. The resulting combined action, when performed, performs the first action, then evaluates the function with the first action's return value, then performs the second actio