The one that finally made it click, was from a very senior Haskell dev who said:
"People overcomplicate things. "Functor" is a fancy word for an interface that has the .map() method, and then "Monad" is a fancy word for a interface that has ".map" and ".flatMap" both"
Or something like that, I think. This probably isn't totally right, but IIRC it's something like this: interface Functor<A> {
map: <B>(
transform: (a: A) => B,
a: A,
) => B[]
}
interface Monad<A> extends Functor<A> {
flatMap: <B>(
transform: (a: A) => B,
a: A,
) => B
}
Which helps me to think about it and understand it. None of the other explanations made any sense.The map function needs to return a Functor<B> and flatMap takes a function A => Monad<B> and returns a Monad<B>.
That's actually not 100% correct either as those are in fact higher kinds, so it's more like you have a generic F<_> that can be a functor or a monad or whatever, and the signature for functor map actually returns a F<B>, and flatMap takes a A => F<B>, returning a F<B>.
It's quite easy to grok looking at Scala.
We shouldn't forget the associated "functor laws" and "monad laws", but they're pretty intuitive and not much different than more mainstream rules; e.g. a 'Serialize' interface might have a "law" like 'Serialize.load(Serialize.save(x)) == x'
What monads don't let you get at -- at least, not without more knowledge about which monad you're using -- is whatever other information given alongside the Ts it contains. If I've got a random M<T>, and I don't know that M is Option, I can only operate over all the Ts (the either zero or one of them) it contains, but I can't tell whether it has any Ts in the first place.
The difficulty of understanding the concept is blurred because both the implementation and the mathematical object are called "monad", but they are technically different. The implementation of it could be simplified as many do, but it still doesn't explain the actual math object's definition. The implementation is not as abstract as the math object, which means it is also not as powerful, so it becomes a limited understanding by definition.
From my perspective, CT is the math of abstract composition, and is foundational mathematics from which other maths can be derived. This highly abstract nature of it, makes it hard to "simplify" further with an explanation. Monad is already by definition as simple as possible, and it's confusing that its implementations in programming get the same name (although I'm not sure naming it differently would help either).
Purists say that it's not correct (monadic laws and all that), but it's a vastly more approachable explanation than literally any monad tutorial.