After using the language for two years I find that the types are actually enough to understand a new library, however, am taking for granted that it's an acquired skill.
Looking back I do recall being in the same position as you, someone who just wanted to wrap their head around something and get an example working. I was stubborn and plowed through things I didn't understand until it clicked, but do realize that not everyone is as hard headed as myself.
So my basic impression is: as soon as you know where the library fits in and how it works and even more importantly why it is doing things the way it does, you can get by just by using type signatures. But that knowledge is usually hidden behind a myriad of half-done blog posts and well-meant tutorials, which introduce you to the what but still assume a good deal of knowledge about why. And the rabbit hole gets larger with every dependency boiled onto your actual study target.
It's not by any means impossible to get in, but I think it could get a lot better. Just making a habit of merging tutorials and introductions into the module's documentation would be a great step forward. A mandatory ELI5 section might do wonders :)
I think this overstating it, unfortunately. I'm an intermediate Haskeller, and when I tried to use `hasql`, I found the lack of documentation to slow me down.
It's a testament to the power of types as documentation that I was able to use it at all, but examples and simple cookbook-style "Here's how you do this thing" or "Here's how you use this component" or "You can't do this because the interface doesn't allow it; here's why" would have sped up my acquisition of the library immensely.
It's all in all a very bad way to do polymorphism and that's what the article points out. It's especially odd in a language like Elm, that obviously is supposed to be like other ML-like languages.
http://softwaresimply.blogspot.com/2016/12/on-haskell-docume...
I think it's just that the gap to get proficient with Haskell for an imperative programmer is a hurdle they are blind to (I was blind myself!), and they assume a few simple tutorials will bootstrap them into the language like it did the other handful they learned.
This is clearly an education problem, but I guess we still have a ways to go to get acceptance.
Yeah, my intent wasn't to suggest that all non-haskell docs suck. But to show how other languages are much more howto/tutorial oriented, which I didn't appreciate as much before I had to look at ACE recently.
Exactly.
Quick, tell me what this function does:
foo :: Num a => a -> aYou're never given only the type signature however, you also will have the source code. It's very likely that foo is no more complicated than something like:
foo :: Num a => a -> a
foo n = n * 42 + 1
If foo is as complicated as that, then a sentence or two explaining what it's purpose is would be helpful; explaining the "why" of such an odd function would be good. The documentation probably can't say "what" foo does any more clearly than the source code however.If the function is reasonably named, like:
double :: Num a => a -> a
double n = n + n
then having some documentation that says "Doubles the given number" isn't going to add much value, and might become out of sync with the actual code. I appreciate that all the Haskell documentation I have ever encountered has a links to the source code throughout.Aside: When I look at the definition of an unknown function in Haskell, I feel like I'm at the top of an hierarchy. The unknown function may be comprised of other unknown functions, but the entire structure of what is happening is present. I know what all the variables are, etc. When I look at the definition of an unknown object-oriented function I often find myself in the middle of an inheritance stack with implicit behaviors and variables being inherited from above, and unknown functions being called below, and it's more confusing in my opinion.
foo :: (BarMonad m, BazApplicative b) => ConfigurationStructure t -> (b -> m t) -> [b] -> t
You can figure out a little of what it does, but how that fits into the application it supports, and what exactly it should be used for is non trivial.Haskell programmers (whose numbers I count myself amongst) saying that "the types are the documentation" is like expecting someone to build a lego model from the picture on the box, and saying "well the studs are the documentation of how the pieces fit together". It's correct, but it misses all the nuance of how the functions should be composed, not just how they can be composed.
So the type claims. Without looking at the implementation or using `-XSafeHaskell`, you don't know if there's an `unsafePerformIO` call lurking.
/pedantry
So within the context of this thread, type signatures vs hand written documentation, I would say your point is a +0 for hand written documentation. Both type signatures and hand written documentation can lie.
It's basically Haskell code smell. Other languages we are used to do not allow exact precision when talking about input and output types, that's why we don't think about overly-generic function declarations as smelly, but they are.
Types can be a useful part of the documentation, but if your program is the least bit interesting, you will have parts that you should properly document. This is so much more true for libraries, especially those that adhere to a more mathematical style. If you write such a library, you should also write a paper explaining the library.
words :: String -> [String]
If you say it's already obvious, I disagree, since this code prints "1": main = print $ length $ words "jack am I"
(The spaces in the string are six-per-em unicode characters)In this case, I'd try something like this:
words :: Sentence -> [Word]
(Sentence and Word being aliases for String)A function named `words` with `Sentence` as a parameter implies a common-sense do-what-I-mean function, like breaking on all sensible whitespace. With common sense in play, it's your task to make the function behave like people expect it to (which differs per person of course). If you choose not to, don't write `words`, write a `split` with a parameter for character classes (or indeed a boolean function working on a character) the split should be performed on.
(PS. I assume you are implicitely critisizing Prelude's `words`, which breaks on anything that's `isSpace`, which is explicitely defined. Its definition is way out of my competence, there are probably good reasons for not including thin spaces. If not, they could probably get included with a due process -- with common sense, it's always an iterative process till it's done)