I'll just add in the Cons: no ad-hoc polymorphism. This is what causes ocaml to use different operators for each type (+, +., +|, etc), which is what most beginners don't like about the language.
Delimited overloading[1] is a nice progress on this front that I wished I knew when I started using ocaml. Though I admit you can later appreciate the benefit of not having ad-hoc polymorphism in several situations.
I honestly haven't been bothered by the lack of ad-hoc polymorphic.
I'll add to the cons though just for completeness.
- The standard lightweight way of achieving higher ranked polymorphism is through record types, which is strange.
type wrapper = {call: 'a. 'a->'a}
let myFun x = (x.call "asdf", x.call 10)
As far as I know (someone correct me) both Haskell and OCaml require that you do something special to indicate this level of polymorphism, so that in itself is not a "Con" for OCaml, but there could have been a way to annotate the argument to the function itself as a polymorphic function without needing the intermediate record wrapper type (I believe Haskell has this feature). A benefit of the OCaml approach is that you get to pretend that HM inference is powerful enough to infer that degree of polymorphism and not annotate your function arguments (because record types must be predeclared) and it recognizes the type of record based on the `.call` access.
- The syntax is kind of strange some times. Some of this is because they've eliminated the requirement that every `let` have an `end`. That does end up making other things more pleasing to read:
let x = y in
let p = z in
expresion
- There are three ways to declare functions.
Syntactic sugar for curried function. Each argument allows one pattern matching case inline.
let f x y =..
let f (x1, x2) (y1, y2) = x1+x2+y1+y2
Same as syntactic sugar above but anonymous. Still only one pattern match per argument.
fun x y -> ..
fun (x1, x2) (y1, y2) -> ..
Anonymous, non-curried function (which only allows a single argument) but allows multiple pattern match cases on that single argument.
function Some x -> "1" | None -> "0"
So there's some intricacies to the syntax, but these are pretty mild annoyances and they have other benefits that aren't immediately apparent. Something as trivial as syntax shouldn't be something that you base your language decision on, especially since OCaml has always allowed highly configurable (almost arbitrarily configurable) syntax via CamlP4 (and now even simpler extension points).
Delimited overloading[1] is a nice progress on this front that I wished I knew when I started using ocaml. Though I admit you can later appreciate the benefit of not having ad-hoc polymorphism in several situations.
[1] http://pa-do.forge.ocamlcore.org/