Maybe I should try again not that I'm a bit older and more experienced...
Maybe I should try again not that I'm a bit older and more experienced...
Consider the following code where you would like to multiply a number by two, but this number is potentially undefined:
var x = ... // a number or null
if (x == null) return null
else return x * 2
Now, imagine if x were a list containing zero or one numbers (if the number is undefined, the list is empty). In this case, you could refactor the code as follows: var xs = ... // a list of zero or one numbers
return xs.map(x => x * 2)
The advantage of the second snippet is that there are no branches, thus making it easier to reason about the code.Thus, in order to understand 80% of their utility in 20% time, I can recommend that you think of a monad as a wrapper around a value which allows you to treat special cases uniformly. In this case, a list of one or zero items. Of course there is more to it, but I this is usually enough to get people to stop worrying and interested in exploring more.
For this same reason, my favorite explanation of monads is actually https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...
Why? Because it does not explain monads at all. In fact, it does not even have the word "monad" in it. But the whole mess about "function coloring" brought up to describe async code is basically the same thing as the "mixing crack and heroin" problem in the original post here - at least in terms of developer experience. JavaScript has two built-in, uncustomizable monads: imperative and async; with the async monad being a superset of imperative but being harder to call and manage.
Rust has at least four: normal imperative code, Option, Result, and Future. We can't really talk about Monad<T> as a type in Rust because its trait system lacks higher-kindedness; so no monad transformers or colorless functions. But they all have combinators that let you sequence functions and store state away in the type. Option/Result let us hack exceptions into a language that does not have exceptions[0], and Future lets us hack user-mode/green threads into a language that is committed to OS stacks.
All the monad tutorials fail because they try to walk you through the underlying math that makes monads work in a functional language. This is wrong; nobody needs to understand the category theory behind the curtain even if it seems super-cool. It's just a hack to force ordering and state into a language that does not have it.
[0] panic!() does not count as an exception mechanism, even though it can be implemented with stack unwinding. There are panics that don't unwind (stack overflow) and you can compile programs that terminate-on-panic instead of unwinding.
IME the biggest hurdle is convincing yourself you've actually understood them, because they seem so mundane that it's weird they even have a name.
From there, it comes with practice and you can see how bind/return work for other things (notably concurrency). There's no need for any theory to use monads effectively.
To me, the problem with Haskell is that you need monad from the beginning, whereas in OCaml, you can start using them when you're familiar with basic ML.
https://adit.io/posts/2013-04-17-functors,_applicatives,_and...
To give you an analogy, tensors are maybe more common in Python than in other languages because of the datascience happening in that community, but that doesn't make tensors a Python thing, and Django folks probably don't care about it.
An example of monads being used in other languages would be all the stream libraries having a flatmap operation somewhere. They don't call it "monad" so they can't brag about it on Twitter, but they're still using it.
Monads are from category theory, a branch of math.
Category theory is the study of generic, simple patterns that work in many different contexts. The insights from recognising those patterns and how they fit together helps to find common ground between different areas of math that initially seem distinct. It's like a kind of OOP and pattern language, but for math instead of programming.
Some patterns got names like Monad and Monoid because they were seen to crop op often, and useful insights could be gleaned from recognising the same pattern in different contexts.
Then someone doing functional programming (FP) realised that the Monad pattern from category theory could be used as a good generic structure in FP to encapsulate many different patterns that were already being used in FP another way.
Things like IO, state, efficient arrays, controlled parallelism, even kernel-like scheduling and user interaction, were already being done in pure FP before the introduction of monads. And before Haskell, which itself is quite old (~32 years). So FP didn't need monads. You just threaded all your functions together "by hand", using things like continuation-passing style or accumulators. There are several ways to do it, and it was possible to write large and sophisticated program this way.
One day, someone realised that much of this threading things together like IO, state, etc could be done in a generic pattern called Monad with a signature (like a generic or interface in OOP) equivalent to the rules of Monad in category theory from math. They also developed the idea of monad comprehension syntax, like a list comprehension but generalised to any monad.
Haskell was fairly new then, but it already worked fine without monads. These new-fangled FP monads and associated syntax so excited people in the Haskell community that they added them to the language, and redid the standard library around the concept. (Like the way Python initially didn't have list comprehensions, or async, but after those were added to the core language people started to use them a lot.)
Nowadays, monads are seen as a functional programming thing, but really there are many FP languages that don't use monads, and monads aren't just for FP. They aren't necessary, but they are a nice pattern that helps write clearer, longer programs that carry things like state and side effects in a pure-functional world.
The reason there are so many different kinds of Monad in Haskell, not just IO, comes from the same reason they were identified in category theory: They name a basic pattern that occurs naturally in many different contexts where it's not initially obvious. Once named, you can combine them. Unfortunately this leads to some confusing tutorials in Haskell, because Monad is a generic (in the OOP sense) abstraction for many kinds of "threading things together", which covers a lot of things you may write that seem very different from each other.
kinda.
they are needed where pure functional and lazy evaluation converge
in a pure, lazy language, you compute things based on dataflow
but what is the dataflow of a keystroke from the keyboard? you need an abstraction to get stuff like this into a pure, lazy programming environment
that's all monads are...abstractions to solve a problem