Functional programming has its downsides. It tends to result in heavily nested code.
Beginner functional programmers nest their code too deeply, in exactly the same way that beginner C programmers nest their code too deeply. It's not an inherent property of the language.
It's hard to fan out results, so programs tend to be trees with a single result.
I have no idea what "hard to fan out results" is supposed to mean, but the conclusion is trivially disproven by the existence of lots of functional programs that do more than compute a single result.
Persistent state and I/O don't fit well with the functional model.
Persistent state works fine, it's just managed more explicitly: you take the "previous" state as input, and you return the "next" state as an output. Practical functional programming languages have a "functional update" syntax for struct/record types that makes it easy to do this sort of thing.
I agree regarding I/O. But OCaml is not a purely functional language, and the I/O implementation uses imperative syntax. It works just like it does in C. (Well, with the notable exception that printf actually type-checks its arguments at compile time.)
OCaml has some features that ought to be in more languages, like Djykstra's "guards" for choosing multiple alternatives.
The "guards" are a minor detail compared to the use of pattern matching for operating over sum types. It's the presence of a first-class sum type which makes these languages so effective at a wide variety of practical problems, because it gives the programmer a truly effective way to express the invariant that "either the data is of this type, OR the data is of that type, but not both". This is not mathematical bullshit, this is basic business-logic programming that happens all the time. In the mainstream programming languages, you don't have anything better to work with than a tagged union; that's a crappy solution.
The article, of course, elaborates on this. You should consider reading it.