Earlier quoted context omitted.
Rust uses a sum type for most errors (Result ) and there are common macros such as "error-chain" that apply a monadic approach to binding/combining them (or chaining them, as the preferred nomenclature in that case). Exception-based languages don't look monadic at first blush, they definitely aren't Sum types, but try/catch is one of the earliest and oldest of the monadic binding/combining approaches, even if it isn'…
>every other line of "best practice" code is `if err != nil` and the only other advice is "well you could just ignore errors". You have a valid point there, but it hurts my ears a little for you to use the word "monadic" or "monad" to make the distinction between how the way Golang handles errors differs from how other modern language do it. To me, for a language to "have monads" requires the type system to give the…
That's one level of confusion in this conversation so far. However, the other problem is that monads don't just mean "computation". (Arguably the Lambda Calculus is way closer to the metal and more accurate to describe as the abstraction of "computation".)
Monads are useful for describing "computations as values" but that's not what the abstraction is entirely for and thinking of monads only as "computations" is how you fall into the easy mistake to make that IO in Haskell is the only "real" monad. There are lots of monads in the wild (Maybe, Either, Promise, List, ...), some of which don't really describe computation at all. We love to mock it, but the technical definition of a monad is "a monoid in the category of endofunctors"; nothing about that is "computation" specifically. Computation is a handy shorthand, of course, but its confusing one of the trees for the rest of the forest sort of thing, mostly because Haskell's IO was the first time monad escaped as a term of art for the deep abstraction that it is.
There is an importance to the monad laws and the two monad "operators", more importance than "computation" as a shorthand for thinking about monads. These operators are called "multiplication" and "identity" in raw Category Theory, but in programming "identity" is usually called "return" and "multiplication" is often called "bind", "bind" is interesting because it also has a dual generally called "join" and "join" is sometimes called "flatMap" or "SelectMany". (Yes, that does point out that most list structures in every modern programming language provide monad operators and obey the monad laws. Lists are a monad.)
A dumber, better shorthand for monads may be "glueable". Monads bind things together in a way that two binds of the same type of monad results in just one monad. (flatMap two lists together and get one list back.) The power of monads is that you can keep gluing things together. Whether by "executing side effects" in a "computation runtime" like the stalwart IO monad or in all the "boring" ways of Lists and Promises and Maybe and Either (that mostly have nothing to do with "computations", except Promises, sort of). The big useful bit is that the Monad laws imply that you can do all this "gluing" in a stable way. That stable way also makes room for the means to transform from one type of Monad to another (Monadic transformers).
Monadic transformers are where some of the real power of the IO monad as an abstraction lives (and other monads in terms of compile-time representations): the idea that the "notation" doesn't matter so much as the end "glued" result. Imperative looking do-notation is the same sort of "glue" as a lot of individual calls to the Monad operators bind and return, transforming from one form to the other is "relatively" "trivial" because the Monad laws say everything should be fine.
There is a usefulness to the IO Monad in Haskell and it is an interesting way to "describe side effects to perform", but the whole of monads and monadic binding is a lot more than just the IO Monad and if you are defining monads to only mean "the IO Monad" you are missing some of their other usefulness.