OCaml makes monads almost invisible. They're much more in-your-face in Haskell. I'm not sure which one I like better, but it's nice to have a plain ol' for-loop available in OCaml.
Perhaps you mean that one doesn't need monads as much in OCaml since it's impure and so they aren't required to manage state?
The one humongous difference is that Haskell is non-strict and this changes everything.
Which just goes to show that monads are actually totally natural. Force someone to live in one all of the time and they just forget it's there.
The major thing is that such monads don't play nicely with laziness which drove Haskell to develop (partial) purity the way that it did. This plays out in OCaml where you have to explicitly say exactly when you're forcing lazy thunks which lets the sequencing of operations remain clear.
How is that different from saying "mutable state is natural?" I mean, if you don't see the monad, aren't we just right back at imperative programming, or is OCaml implemented with real monads under the cover that make a significant difference to the programming experience? Does it even have a basic effects system?
So, you can think of OCaml's semantics as living inside of a monad. Or just do it all implicitly by giving basic semantics to ;.
And the way I was speaking I mean to say that "mutable state is natural" too. I don't know that it is universally, but it's certainly something that makes 90% of programmers today feel comfortable.
Indeed side-effectful computation can be represented as a monad, and therefore the ; in ocaml might be interpreted as a bind. Something just needs to satisfy the monad laws in order to be a monad. However, in Haskell the IO monad is explicitly defined. This is very important. The do syntax is sugar over monadic binding them together. In ocaml, sequential computation is simply scheduled sequentially.
In haskell, something of type IO () is a VALUE which corresponds to an ACTION which can be RUN. The function which returns the IO action is still pure. It is the outside world which consumes the IO action and performs it. In o'caml, functions are merely impure as in other languages.
The function that prints 3 and returns 8 in ocaml has type "int". The function that prints 3 and returns 8 in haskell has type "IO Int"...
That is not true at all. If you have worked with any monadic data structure that is not hardcoded into the compiler (like List) for example Lwt you would see how tedious handling monads is. So tedious that not one but four syntax extensions exist to make it less boilerplate heavy (pa_lwt, lwt.ppx, pa_monad, omonad).
Specific instances of monads are definable in most languages in use today, it's when you want to generalize over monads that Haskell stands out. For that you want higher kindedness, Ocaml can simulate them using the module system but it's rather cumbersome. There is a work around described here: https://ocamllabs.github.io/higher/lightweight-higher-kinded.... The work around is also applicable to the cousin, F#: https://github.com/palladin/Higher/tree/master/src/Higher.Co...