After they’ve been working for a month or so, they’ll “get” monads on a gut level, and the details won’t be so confusing to them.
Explaining monads in terms of promises and futures will make a beginner associate them with asynchronous programming, which might make some sense for some monads. But now show such a beginner a simple list comprehension and explain it is also a monad, and I assure you they will be very confused! Explaining concatMap in terms of promises is a very convoluted way to explain something quite simple.
Haskell comes from a population that loves to understand things from the ground up, and most people learn by doing.
So, what if, instead of an abstract burrito metaphor, we taught people 4 monads: List, Maybe, State, and IO. We explain how do syntax does “something different” for each of them, and how IO is very similar to promises, but the others aren’t. Then we let them play with those for a few weeks, submit some PRs, let the panic of not understanding subside, and then teach them the theory?
Think about how JavaScript developers learn promises. They don’t understand how they work under the hood at all when they first start using them. Then, when they need to go deeper and look under the hood, that theory is connected to their practice, and makes much more sense to them.
Yes, I am on board with that.
What these languages don't have and Haskell does is a way to talk about all monads, all at once. Instead of
replicateM :: forall m a . Monad m => Int -> m a -> m [a]
you have to separately implement replicateArray: <T>(count: number, ts: T[]) => T[][]
replicateFunction: <S, T>(count: number, f: (s: S) => T) => (s: S) => T[]
replicatePromise: <T>(count: number, t: Promise<T>) => Promise<T[]>
and so on.
No you don't. You are confusing monads with features which can be implemented using monads. In Haskell monads are used for modeling side effects, but this is not the case in other languages. In Haskell exceptions are implemented using monads, but this is not the case in other languages.