I'll give what I consider to be reasons why the language isn't more popular, but it seems most people that use the language don't share these views. Please don't flame me for the excessive rant below and keep in mind these are subjective reasons (that may be badly worded, (it is 2am) or even worse simply misinformed).

- The compiler error messages are awful, especially for newer programmers. By awful I really do mean awful. "Syntax error" is not a very meaningful message, but at least it tells you the location right? Except due to the poor grammar of the language (I did say these objections are subjective) a small mistake like a missing in or semi-colon results in a wild chase trying to figure why you are getting a "syntax error" in a perfectly valid piece of code.

- Unpolished language grammar. There are just a lot of small things that bug me about the language like the existence of "in" for let statements (Rust's approach feels much cleaner), having multiple ways to create blocks (begin..end, and parenthesis), having to type rec for recursive functions, difference between declaration of functions in mli and ml, argument sigs and function sigs use the same delimeter, let is used for binding functions, imperative mode using refs is just plain ugly. These are minor and almost entirely subjective objections, but the real issue isn't just a lack of polish, it's that the language is too unrestricted.

- Language is too unrestricted. 1) It's often very difficult to read other people's code because there are many ways to accomplish the same thing. 2) The language almost invites you to have high levels of nested logic (something considered a cardinal sin in imperative programming) to the extent that it's not uncommon to see (or be tempted to write) 6 or even more levels of depth. 3) Easy abuse of lambda functions (fun and function) can make it difficult to even know how many parameters a function really expects. I mean this in the sense that you need to carefully read the code to get an idea of even whether a let binding is a data binding or a function binding. Of course these are all things you learn to handle and manage with some experience, but they really don't make the language an easy pick-up.

- Language is difficult to reason about internally. Let me give an example. OCaml can be very clever sometimes, like when you prepend to a list, that's an O(1) prepend with no copy of the initial list even though it's immutable. To write better OCaml code you need to become familiar with such things (though I suppose that's true of any language) and sometimes it's not easy (for me at least) to guess internal behavior. For example, does "let t = {t with x} in" modify t in place even though t is immutable (assuming we are the same level where t is first declared, i.e. initial t is no longer reachable) or does it create a new copy of t and then modify the x in that new copy? I guess a fair counter is, who cares?, and I get that, but at the same time I have some underlying drive that makes me want to know (the answer is the second one, the data is copied) at least things such as when I am allocating new memory.

The last big one and the most controversial of all - (purely) functional programming is not worth the trouble. It is inherently more complicated than imperative regardless of all the functional people claiming otherwise. It's not as simple as "you learned one first and that's why it's easier", an argument I've heard repeated many times, one that probably has merit, but also one that cannot account for the difference in difficulty. I can write an entire paper on why functional is more difficult (essentially it will use most of the reasoning as Out of the Tar Pit - which argues that functional is great - but with some changes in the argument to result in an opposing conclusion). In terms of informal reasoning, the benefits of lack of internal (to a function) mutable state are essentially zero, or better worded, lack of local mutable state increases the complexity of a function. It's not like your state doesn't change if you use recursion and immutability... it certainly does, it just mutates transiently while you are building up your result. That's often more difficult to reason about than a typical imperative solution with mutability. Okay, fine, perhaps I am a shit developer who simple isn't smart enough or lacks the experience, but the reality is when you are working on a team, the chances are that you will have a very difficult time gathering 20, 30, or more solid functional developers let alone that many OCaml developers. Ideally, you want your team to all be at least competent in the language you are using and that's harder to achieve with OCaml.

I think OCaml is a lovely language that brings a lot to the table, but I simply can't see it becoming mainstream (or really any other functional language either). The thing that makes sense to me is to merge imperative and functional into some sort of hybrid language, which is one of the main reasons Rust is so exciting. Reduce global mutable state to as low as reasonable while making allowances for local mutable state. Also Rust fixes the three sections of complaints above - great errors and warnings, feels more polished when writing, and forces a much higher rigidity - though it introduces some other issues.

At this point I am not even sure what I'm talking about so I'll end it here hoping the above made any sense at all.