* Ocaml has an extremely powerful module system that puts that of almost every other language, including Haskell's, to shame.
* Ocaml also has a very powerful object system that goes unused 95% of the time, but could potentially be useful for that other 5%; there's nothing comparable in Haskell.
* Ocaml has no ad-hoc polymorphism, whereas Haskell has typeclasses. The module system substitutes for this in many cases, but it's arguably less ergonomic. In other cases you just need to, say, use different operators to add floats than integers, which is definitely less ergonomic.
* Ocaml has comparable - probably superior - single-threaded performance (very good), but unlike Haskell there's no real parallelism story to speak of. Multicore support has been in the works for over five years and still hasn't arrived.
* Ocaml has a smaller community and ecosystem. Yes, smaller than Haskell.
* As a matter of personal opinion, I prefer Ocaml's straightforward and widely-loathed syntax to Haskell's weird, whitespace-sensitive one.
Subjectively, I think OCaml is a huge improvement over, say, Java, but if you’re deciding between learning one of OCaml or Haskell in 2020 I think Haskell is the better option for most people.
I think Haskell's a good way to start learning functional programming because it really beats the habit of loops and mutability out of you real fast by making the functional way easier. But if you're already an experienced FP programmer, less restrictive languages are better.
What makes Haskell interesting is all the crazy optimizations it can do since the compiler has so much information about the program. It would be really cool if other languages had a "pure" annotation or keyword that signified the function has a pure interface. This could dramatically improve the performance of a lot of Scala/F#/OCaml code.
> I've personally never found Haskell's encoding of mutability or impurity into the type system to be of any real benefit.
I find it completely indispensable for large codebases, especially codebases I didn’t write.
> Most of the time your impurity is coming from logging, networking, or interacting with files or a db; in which case, it's entirely obvious what's going on.
Maybe you’re a genius or something, but to me (and most people) it’s not “entirely obvious what’s going on” when large codebases use impure side-effectful semantics in a way that’s not clear from the types.
> Other times, you have a pure function that's implementation is unpure, which is also obvious on what's going on.
Once again, it is not at all obvious. And a very large fraction of the time, that “pure function with an impure implementation” isn’t actually so pure after all, and either leaks mutable semantics or behaves badly (e.g. if used in a multithreaded way). Haskell actually does have a real solution for this in the form of the ST monad. You can write impure things and prove (using the type system) that they have pure semantics. I’m not aware of any other language offering this.
> In any case, if you're programming in good FP-style all Haskell is providing is a bunch of headaches around finding the write way to make all your type annotations match up
Not only are type annotations optional (and usually trivial), but you can get the compiler to write them for you interactively e.g. with holes. In any case, this is never a problem in practice for “business logic” code. I’ve only really seen this come up e.g. when writing lenses by hand (i.e. rarely).
> it's kind of a annoying experience when you're just trying to get something done.
The point where the cognitive overhead introduced by Haskell’s type system is worth the payoff compared to, say, Python, is about 1-200 lines in my experience.
> But if you're already an experienced FP programmer, less restrictive languages are better.
The only “improvement” that you’ve cited so far is that “less restrictive languages” let the programmer be architecturally lazy, which I don’t think is really an improvement.
> It would be really cool if other languages had a "pure" annotation or keyword that signified the function has a pure interface.
If this were preferable, then Haskell programmers could just write everything in IO and selectively factor things out into pure functions. The fact that no one does this is, I think, telling.
That got a smile from me, given that OCaml is older than Java.
For concurrency, LWT and Async are both mature and used in production.