3 ms·
I'm not an expert on promises, but they certainly seem like monads. A promise, if I recall correctly, looks something like this: doThing1().then(function (
by thinkpad20 12y ago
I'm not an expert on promises, but they certainly seem like monads. A promise, if I recall correctly, looks something like this:
doThing1().then(function (thing1Result) {
doThing2(thing1Result).then(function (thing2Result) {
doThing3().then(function() {
doThing4(thing2Result + thing1Result).then(function (res) {
console.log("I did a lot of things. The answer is: " + res);
}
}
}
}
Basically, at each stage we're calling a function which may or may not return immediately, and saying "whenever you're done calculating, pass your result into this function I'm giving you here." Similarly, in monadic computation, you sequence a series of "actions" together by passing each a function that determines what to do based on the output of the previous one. In Haskell, the above fake code would be written like this:
doThing1 >>= \thing1Result ->
doThing2 thing1Result >>= \thing2Result ->
doThing3 >>= \() ->
doThing4 (thing2Result + thing1Result) >>= \res ->
putStrLn ("I did a lot of things. The answer is: " ++ res)
So you can see the `>>=` acts like the `then` function we saw before, and each time we're passing in a new function (indicated with a backslash, which resembles a lambda). And since this kind of thing is so common, there's a syntactic sugar for all of this:
thing1Result <- doThing1
thing2Result <- doThing2 thing1Result
doThing3
res <- doThing4 (thing2Result + thing1Result)
putStrLn ("I did a lot of things. The answer is: " ++ res)
Hopefully it's simple enough to see the correspondence between the above snippet and the one before it; the two are essentially identical.
Now the really cool trick is that the same concept holds up for any monadic computation; that is, any situation in which we have a series of steps, executing in a well-defined order, and at each step we have the ability to "examine" what the previous step did in some way. Thus, for example, we can model a series of fail-able computations, where the failure of any one in the chain should cause the chain to stop at that point; or a series of actions that affect a background state in some way, or a chain of effectful computations like IO, or any number of other things -- the abstract idea tends to pop up all over the place, which is one of the great powers of type classes in general: they give you tools that you can use all over the place, even in vastly different arenas.