▲ | ekidd 4 days ago | |
Yes, monads are abstract, but the definition is also very precise. Specifically (using C++/Rust notation for parameterized types), if we have a type "M<T>", we also need:
...and finally the magic bit:
This, in turn, allows defining what you really want:
...where the mapping creates an "extra" layer of M<...>, and then we flatten it away immediately.(There are other rules than ones I listed above, but they tend to be easy to meet.) Once you have flatMap, you can share one syntax for promises/futures, Rust-style Return and "?", the equivalent for "Option", and a few dozen other patterns. Unfortunately, to really make this sing, you need to be able to write a type definition which includes all possible "M" types. Which Rust can't do. And it also really helps to be able to pick which version of a function to call based on the expected return type. Which Rust actually can do, but a lot of other low- and mid-level languages can't. So monads have a very precise definition, and they appear in incomplete forms all over the place in modern languages (especially async/await). But it's hard to write the general version outside of languages like Haskell. The main reason to know about monads in other languages is that if your design is about 90% of the way to being a monad, you should probably consider including the last 10% as well. JavaScript promises are almost monads, but they have a lot of weird edge cases that would go away if they included the last 10%. Of course, that might not always be possible (like in many Rust examples). But if you fall just barely short of real monads, you should at least know why you do. (For example, Rust: "We can't have real monads because our trait system can't quite express higher-order types, and because ownership semantics mean our function types are frankly a mess.") |