4 ms·
IMO one of the best monads for explaining the concept is async promises, especially for JS programmers who haven't always had the luxury of async/await. If we
by frogulis 5y ago
IMO one of the best monads for explaining the concept is async promises, especially for JS programmers who haven't always had the luxury of async/await.
If we have a long-running process, we can block the thread and wait for it to return us a result, but that's far from ideal.
Instead we return a "promise", a structure that we can provide with a callback (using the `then` method on the promise) to manipulate the result of our asynchronous processing.
The types look something like this:
`doSomeAsync(T1): Promise<T2>`
`Promise<T2>.then(T2 -> T3): Promise<T3>`
Let's look at the type of monad `bind`, `>>=` in Haskell:
`M a -> (a -> M b) -> M b`
The types of `bind` and `then` are very similar! `Promise` is (approximately) a monad.
The fact that we can't just "get" the result of the Promise and instead need to provide a callback is a core part of my understanding of the use of monads in Haskell -- they are used in Haskell to represent the result of a non-deterministic effect.
You can't (or shouldn't) just get the value out of a monad, in the same way that you can't just get "the" value out of an array -- it doesn't make much sense to talk about.
The use of monads to represent non-deterministic effects is a software engineering choice. The "monad" itself is just the structure of computation where you have a wrapped thing `M a`, to which you apply a "callback" `a -> M b`, to receive a monadic result `M b`. There are also some laws which a well-behaved monad needs to follow.
The point of this is that even though that `b` might never eventuate, or might come about later, or might be multiple values, the `M b` can be treated as a plain-old function result and the type checker is able to make stronger guarantees. Pretty handy when your language is based around pure functions.
Side note: async/await is pretty much do-notation for Promises. You write imperative-looking code and it gets magically turned into asynchronous callbacks for you.
- frogulis 5y agoI added some content and fixed short errors to turn this into a webpage here: https://frogulis.net/writing/async-monad https://frogulis.net/writing/async-monad
- pgorczak 5y agoYeah, Promise really helps illustrating these concepts. Also while being less explicit in the syntax, the combination of Promise and Promise.then forms a functor that - maps type x to type Promise<x> - maps function x -> y to function Promise<x> -> Promise<y>
- frogulis 5y agoOh hmm, that raises a good point. I guess the types I provided in my comment are actually for `then` as `map`, not as `bind`, oops. Both of these exist: `Promise<T1>.then(T1 => Promise<T2>): Promise<T2> // equivalent to bind` `Promise<T1>.then(T1 => T2): Promise<T2> // equivalent to map`