2 ms·
Haskell has a lot of problems, but this article is not good, and does not describe them well. It reads like it was written by someone who has tried Haskell, and
by ilikebits 6y ago
Haskell has a lot of problems, but this article is not good, and does not describe them well. It reads like it was written by someone who has tried Haskell, and has sort of written some code in it, but has mostly been put off by bad and confusing tutorials, and has never really grokked the language. Indeed, look at the author's GitHub - of their 50 projects, only 1 is Haskell, and that is a handful of commits before giving up.
My main complaint with Haskell is that it's actually a great language, wrapped up in (1) hilariously beginner-unfriendly syntax and tooling, and (2) a library ecosystem that does not quite understand the value of friendly documentation (although this is actually improving a lot). There are also some weird warts around the language itself, but those are mostly tolerable and not so much worse than the weird warts in all other languages.
My specific complaints about beginner-unfriendliness in syntax and tooling:
- The compiler will infer the types of names as generally as possible, and will run this inference both forwards ("this variable was assigned a string, so must be a string later") and backwards ("this variable is used as a string, and therefore must have been a string earlier") depending on what information is available for inference. This means that type errors are hit-or-miss; about 60% of the time, the type error will point you to your actual mistake, and the rest of the time it will point you to where the type inference algorithm got stuck in unification (this may not be the location you made your actual mistake, because the algorithm will try its best to infer concrete types working under the assumption that you didn't make a mistake, so it sometimes gets stuck trying to unify types at an earlier/later point than your actual mistake).
- The syntax is VERY minimal, and prefers minimalism over readability. In particular, Haskell has no explicit parentheses for function calls, and has implicit currying! This means that it is very easy to accidentally get the arity wrong in a function call (for example, `foo (bar baz) quux` and `foo bar baz quux` mean very different things!), and create an extraordinarily confusing type error (in the best case, an error about applying things to the wrong number of arguments; in the worst case, errors about constructing infinite types).
- This minimal syntax also means that you can sometimes type something that is _almost_ correct, but is actually valid syntax that means something totally different! Fortunately, your almost-correct expression will not type-check (so you won't ever mean something else without getting an error), but it's still a head-scratcher in the beginning to figure out exactly what the compiler is taking your code to mean. In some of these cases, you may even type something that is not syntactically valid vanilla Haskell, but _is_ valid Haskell-with-some-extension, and the compiler will unhelpfully suggest that you turn the extension on even though it may not be what you want (and also provides no explanation of the extension).
- The compiler tells you what expression has a type error, but never actually tells you the exact term that has a type error. You have to sort of guess at this by understanding the rough high-level of how the type inference works. It's something you have to really pick up, and is hilariously unfriendly compared to e.g. `rustc --explain`.
- Print-debugging works differently than what you'd expect. Nobody ever teaches you that `trace` is a thing (seriously, this should be thing #2 that is taught), and nobody ever teaches you "you're getting trace messages in a different order than you expect because Haskell is evaluating expressions with graph reduction, not in the order of the lines of code you wrote").
Really this boils down to: even though Haskell will do a very good job at preventing you from holding it wrong, you really do have to have a decent grasp on what you're doing to write a program. As a complete beginner, you can't just put something you think will mostly work and then rely on simple compiler errors to tell you where to spot fix (unlike e.g. Go, Java, etc.). Fortunately, this gets much better over time - you learn how to interpret compiler errors better as you get a grasp on the language, and you learn tricks for building incrementally correct programs up using `undefined` and friends.
My specific complaints about documentation:
- Contrary to this article, types actually ARE decent documentation in Haskell. The constraints you can do with types in a pure language are much, much stronger than in other languages you've used before. Trust me - this is a Blub paradox thing. It's just a qualitatively different kind of type safety.
- Unfortunately, although types do a great job at preventing me from plugging the pipes together wrong, they still don't tell me anything about the _semantics_ of a function. Lots of older libraries do a very bad job at documenting semantics, but this is getting a lot better.
- Arguments in Haskell functions are not named (or at least, their names are not exposed in documentation). This makes for some confusion sometimes - what am I supposed to pass as arguments to this `foo :: String -> String -> String` function?
- Lastly, a lot of libraries document very academically. Here's an example: https://hackage.haskell.org/package/logict-0.7.0.3/docs/Control-Monad-Logic.html https://hackage.haskell.org/package/logict-0.7.0.3/docs/Cont.... The documentation here is written assuming you understand how Logic computation works, provides no examples, and you're supposed to read the module documentation and actually go and _read the paper_ (in fairness, it's pretty short) that they've linked. This is a far cry from the NPM world (which, for all its faults, has really embraced accessibility in documentation), where everybody has a quick start.
Overall though, it's a really good language. Once you get monads (and a HUGE part of this is not the idea of monads, but the really unfriendly syntax and error messages of Haskell; IMO the _real_ lightbulb moment is grokking the difference between data, types, data constructors, type constructors, and typeclasses), a lot of things make a lot more sense. The testing story is amazing, the refactoring experience is amazing, the incremental development and REPL stories are great, and there are some truly astounding abstractions you can use that are just impossible to express in other languages. It's my regular side project driver, and my team maintains a Haskell codebase in production.
Lastly, a word of advice to anyone looking to actually make a good-faith evaluation of Haskell: try it, make something substantive with it, and stick with it until you actually grok it. Ask for help from the FP Slack! Seriously, don't just pick it up, get stumped by error messages, and give up. The tooling is very beginner-unfriendly, but fortunately the humans over at the Functional Programming Slack (https://fpchat-invite.herokuapp.com/ https://fpchat-invite.herokuapp.com/) are VERY friendly. As a first project, I'd recommend doing the Advent of Code, or building a small web application (IMO with Scotty, which feels like Express, rather than Yesod, which feels like Rails. Yesod is almost certainly better in production, but has a different and IMO less instructive learning curve). If anyone needs help, follow along with my AoC 2020 (https://github.com/liftM/advent2020 https://github.com/liftM/advent2020), although be aware that some things I do there are a bit fancier than necessary.
I find that articles like this that complain about things like type abstractness or the mathy concept names really are not representative of the actual pain points with Haskell, and are FUD more than useful. Yes, the tooling is beginner-unfriendly. Yes, you will probably need to ask some questions on the FP Slack on your path to learning, things that a friendlier language would have documented. But this mystical aura of "ooh, there are types and they are very abstract!" is not reflective of reality, is not a criticism grounded in the reality of writing Haskell, and IMO is misleading for people genuinely trying to assess the language.