Nothing can prevent a undocumented mess, but certain factors can help mitigate it. For example, most people in Haskell write the type declarations for their top-level functions, which certainly helps. Also, because you need to be honest about side effects in Haskell, if you see Vector a -> Int -> a, you know there are no side effects and so you can be pretty damn sure that this is an indexing function.
Not just is that function pure, but free theorems indicate that the only way you can get an a is by using the Vector that goes in. Sure, it could ignore the Int - we don't have linear types, afterall - but it really does narrow down the "what could this do?" options.
I believe no language can prevent a "jumbled undocumented mess", but it a language can get pretty far just by making paths to make documented and clear code easier than paths to no documentation and jumbled code.
Oh yes! My favorite part of Haskell's type system is it's documentation purpose. It doesn't cover intent very well, but it's quite frequent that I can reason about the entire purpose and functionality of a function just by looking at its type. This documentation is guaranteed to never go out of date.
Definitely. It can go VERY overboard at times, and that's part of Haskell style, writing code that makes intelligible types appear. It means that both the compiler and your users will have an easy time with your code. Most of the best libraries are pretty good about that.
It's also a familiarity thing. The most complex signatures require some practice before they're easy to digest.
See, the issue I have with this is that in my own experiences with other language, this is almost never a problem. I don't need to know "what" something is doing, I need to know "why" the developer did it that way. Haskell seems like it goes out of its way to obfuscate intent with heavy abstraction, avoidance of semantic variable names, and sometimes avoidance of variable names entirely. That seems like exactly the sort of code that I find least comprehensible...
By "what" I mean semantically what---I need to know that this function turns a Tree into a List or a Person into a String. I find this information very useful as it's fairly non-operational. Usually functions are further documented with a bit of intent information.
The avoidance of variable names is hard to judge until you program in Haskell a lot. Variables (or "points") tend to just be used to wire pipes together. These variables are rarely used more than a line or two after their definition and thus short names emphasize this local context.
When people write code that involves variables naming mutable cells or long-lived context they provide long, descriptive names. I don't think anyone disagrees about the utility of that. It's just a rare style in Haskell.
Calling it "self-documenting" might be a bit of a stretch, but I can assure you that having type information in the absence of comments helps a lot -- the base level of no documentation is much easier to work with.
What makes one think that any language can prevent "a jumbled undocumented mess"?