Let's look at the example:
do
a <- getData
b <- getMoreData a
c <- getMoreData b
d <- getEvenMoreData a c
print d
This desugars to: bind getData (
bind (\a -> getMoreData a) (
bind (\b -> getMoreData b) (
bind (\c -> getEvenMoreData a c)
\d -> print d)))
Edit: the above code was initially wrongly desugared.Which is exactly the callback hell criticized in the article.
Sure, monads and do-syntax provide a nice abstraction and are interesting constructs from the perspective of type theory, but in my opinion it's not principally better than Promises or await.