Your tools should be able to tell you that. Can't you just ask your IDE? In cases where it really is confusing you should probably annotate it.
> Annotating is complicated because you're often dealing with complicated types (with nested tuples for instance).
Having types for nested tuples doesn't make them any more complicated than they were already. Sometimes people overuse complex tuple structures when they should just declare a record, but that can happen in any language.
> Using higher-order functions: often you'll see a function with nested functions in it , or some functional list processing function like "unzip" being used . This increases the code complexity a lot ("what does unzip do again?").
In any language you would need to do what unzip does. Having a common version of the function that can be reused in a lot of different places is much better for understandability than writing it out longhand every time.
> Using monads: yet another distraction from what's really happening.
They should do just the opposite: let you shift the distractions in the monad and keep the main code expressing the essence of the problem. Some things are better expressed in straight-through code that just does the things, but you can do that in OCaml too.
> It forces you to write nested functions and doesn't play well with conditions .
It plays well with conditions that are already expressed as functions/expressions, which are easier to think about in the first place. In good functional style you can completely forget about "control flow" because things like "if" become just an ordinary case of ordinary functions.
> Also various people use monadic operators way weirder than >>= and all of this becomes really hard to read.
Naming things is hard, but at least an OCaml "operator" is always just a normal function, so you can always just click through to the definition of it and read exactly what it does.
> Using functional-friendly data structures: the linker is using immutable lists of optional bytes to represent memory images, because this is idiomatic in OCaml. However this is slow and inefficient as hell.
This sounds like a specific bug rather than a general problem. Certainly there are plenty of datastructures that manage to be both functional-friendly and efficient.
> Designing functional loops: when the loop you want isn't in your stdlib, you first need to create a recursive function, and then make sure all recursive calls are tail calls otherwise you blow up your stack. The result is usually not very fun to read.
Does OCaml not have a recursion-schemes-like library? Then you can just fold down your datastructure (because a "loop" is never really about looping, it's about handling a datastructure) and you already have your intermediate representation and you can just write the essence of what to do at each step and it's really clean and lovely.
> tl;dr: the C language is small, the OCaml language is not
Disagree with that. C has all these different kinds of control flow and all these built-in operators, whereas everything you would want to do in OCaml is just plain old functions and expressions.
OCaml does have for and while loops, to be fair, although they don't support break or continue (you can simulate break by throwing in the loop body).