Hello,
Why does the Prelude seem unsafe - or rather why does the Prelude seem less safe than it should be?
Some context - I’ve been learning Haskell over the past few weeks and overall I’ve really enjoyed the expressiveness of the language as well as the concept and implementation of Type Classes and constrained polymorphism in particular.
However, since I’ve had the opportunity to be exposed to algebraic data types, parametric polymorphism, etc. in other languages; and more importantly - sane, logical, and safe ways to handle functions that can cause an application to crash (specifically Rust’s notions of panic, Result, and Option). It confuses me why Prelude contains so many functions that may cause an application to crash.
I’m of course talking about partial functions. What’s confusing (and frustrating) is not necessarily that partial functions exist (that’s a given) but rather how they’re handled (inconsistently) and documented (relatively poorly).
I’m currently using the haskell-language-server
as well as this list from the wiki as my primary sources as to what functions should be “avoided”.
Now I know that comparing rust and Haskell isn’t always a logical comparison seeing as the two languages have vastly different objectives but the error handling story should be very similar IMO. Why doesn’t Haskell have the following:
- Safe variants for all partial functions in the standard library (prelude / base) that return
Maybe
. Ideally the safe variants should be the default and the unsafe variants should have a longer, idiomatic, name that indicates the fact that they’re unsafe. - Consistent doc comments for all unsafe functions (partial functions) in the standard library. What I mean by this - if you look into the documentation for the functions listed in the above partial function list not all of them have a mention of their partial (unsafe) nature in the documentation. On top of that some of the functions have
GHC.Stack.Types.HasCallStack
as a constraint that might indicate their erroneous behaviour… but not all of them. The only somewhat consistent approach to identifying partial functions has been HLS’ STAN hints which help greatly by leaving annoying hints throughout my code that indicate which functions are partial.
On top of partial functions there’s also the matter of non-exhaustive pattern matching rearing its head at runtime as opposed to at compile time (yes I know about -Wall). And I hate to keep making the comparison to rust but the rust compiler simply won’t allow you to write non exhaustive patterns.
Between partial functions, inconsistent documentation, lack of Maybe
producing variants of certain functions, and pattern matching - I unfortunately see a pattern of what I would personally consider IMHO as bad defaults for the language (or perhaps the GHC compiler).
Overall I feel that Haskell is the closest to what I would consider my ideal language. Which is why it both confuses and frustrates me that the language does such a fantastic job of tricking me into believing that it’s safe with it’s beautiful, elegant, sophisticated type system and syntax. Especially when these are not insurmountable problems (at least according to my flawed and very limited understanding of language design).
P.S. I’ve heard of potential language extensions that can be added as well as libraries that can be added to address these issues. If someone could point me in the right direction I’d love to try these out (hopefully they play well with HLS).
P.P.S. Sorry if this comes across as ranty but I’m genuinely confused as well as new to Haskell. Perhaps there are solid, logical, pragmatic reasons as to why things are the way that they are and I simply lack the context to form an educated opinion. I would genuinely love for someone to provide counter arguments and / or historical reasoning as to why things are the way that they are.