Haskell's killer feature here is higher kinded types. do notation is just a syntactic sugar. But (with the exception of Scala or PureScript) almost no other every-day language supports the ability for defining abstractions over higher kinded constructs like monads.
I'm primarily a Scala developer, that also likes Haskell. And you're right.
With the caveat that some languages support an encoding of HKTs. You can encode HKTs in OCaml, Kotlin, or TypeScript for example, and there are FP libraries taking advantage of that. I believe the encoding was inspired by this paper:
It kind of sucks, but it does let you work with a Monad type class, and it's better than not doing it at all.
I'm not sure if such an encoding is possible in Rust. I've been told that F# has a hard time with it, not sure why (possibly because of reified generics). In TypeScript and Kotlin it works because the runtime and the type systems are relaxed and you can just force downcasts if you're in trouble.
> I believe the encoding was inspired by this paper
Just to add some tangential color here: even if you don't intend to use this encoding in production, it can be a fruitful intermediate step in refactoring! I just recently ran into a situation in Java where I had some tightly-coupled concerns for which HKTs were the obvious way out. Since Java doesn't have HKTs, I applied the HKT encoding to get the hard part out of the way, and then discovered a simplification that brought me back into the realm of idiomatic Java. I would not have found the final solution had I not pathed through HKTs -- I actually thought it would be impossible to do this decoupling properly at first!
More color: the paper itself describes this approach as an application of defunctionalization. I use defunctionalization a lot, too, to write an obvious recursive solution and then mechanically transform it into an iterative one. So it's nice to know how to apply it at the type level, too ;)