Compare again:
bind(StatefulFunction<a>, a => StatefulFunction<b>) => StatefulFunction<b>
flatMap(List<a>, a => List<b>) => List<b>
Look at the structure. Don't pay attention to what List or StatefulFunction actually are, that's not important here. The thing to focus on is that if you can provide this kind of general function for something, then that something is probably a monad.I.e. a function that takes in Something<a> and a function from a to Something<b> and produces Something<b>.
So why is this pattern so useful that everybody freaks out about it?
Well, that syntax sugar I showed you at the end, let's look at it again:
do
a <- f
b <- g a
h b
The general idea here is that "a <- f" is translated into bind(f, a => ...). The whole block would turn into bind(f, a => bind(g(a), b => h(b)));
Bear with me. Now, let's look at one more example: bind(Optional<a>, a => Optional<b>) => Optional<b>
Optional<a> is just a data type that maybe contains an <a>. Think of it as a safe replacement for null. So, bind for Optional looks at the first argumenta and passes it on to the function and returns its result, if the Optional is not empty. If it is, bind also returns an empty Optional.Now let' say we have a bunch of Optional producing functions:
getUser(userId) => Optional<User> // user may not exist
getAddress(User) => Optional<Address> // user may not have entered an address
getStreet(Address) => Optional<Street> // address may be weird
Now ok, what would we do in a language without monads? user = getUser(userId);
if (user.hasValue()) {
address = getAddress(user.getValue());
if (address.hasValue()) {
street = getStreet(address);
// do something
}
}
Well, that's not great. But because Optional is a monad and we have that nifty bind sugar, that's how that code would look like in Haskell: do
user <- getUser userId
address <- getAddress user
street <- getStreet address
Much better.More generally, it turns out monads are literally everywhere. The guy that talked about CPS transformed programs being monads is right - that's the continuation monad.
Let's look at that. If you know node.js, you know that its API is basically asynchronous:
fs.open(path, callback)
fs.read(fd, ..., callback)
fs.close(fs, callback)
Now, let's read some file in node: fs.open(path, function(fd) {
fs.read(fd, ..., function(result) {
// do something with result
fs.close(fs, function() {
// file closed
}
}
}
Well, that's ridiculous. But becase we have the continuation monad, we can write exactly the same thing in Haskell: do
fd <- open path
result <- read fd
// do something
close fd
So why are monads so useful? Because they are so general. They can be used to abstract over many many things that exhibit the basic pattern (S<a>, a => S<b>) => S<b>. Not to mention that when you write a function to generally work with monads, it will work for ANY monad. List, State, Maybe, Cont - doesn't matter.There's not much more to it than recognizing that pattern. That's it. Once you see that that's literally it you'll start seeing monads everywhere :)