12 ms·
Pitfalls in Haskell
- panic 10y agoThe Haskell 98 report mentions a strange type system restriction. It is called the monomorphism restriction and concerns making decisions about types during inference. It sounds like abstract nonsense, but makes sense in the context of category theory, where morphisms are a generalization of functions and monomorphisms in particular correspond to injective functions. Regardless of its cryptic name, it solves a practical problem: it prevents ambiguous types from appearing. The "monomorphism" in "monomorphism restriction" isn't the term from category theory: it's just the opposite of "polymorphism". You can read more about the details of the restriction and why it exists here: https://www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-930004.5.5 https://www.haskell.org/onlinereport/haskell2010/haskellch4....
- pklausler 10y agoI don't really mind the monomorphism restriction for definitions whose types aren't functions, but it bugs me quite a bit that a function defined in terms of pattern matching won't trip over the monomorphism restriction but the otherwise-equivalent definition in terms of lambda can.
- Morgawr 10y agoThere are a lot of really annoying quirks about Haskell. It is a beautiful language but after using it professionally for a while, some things come to mind that really make me pull my hair out of frustration (although I understand why they exist). My biggest gripe is record handling. There are a few libraries that help dealing with the pain of records, but if you're using 'vanilla' Haskell, they really can be frustrating. For instance, any field name in a record pollutes the global namespace for that module. data MyRecord = MyRecord1 { name :: String } | MyRecord2 { name :: NameType } <-- This is an error because the "name" field can't be both a String and a NameType (a made up type, just for the example). You need to use something like rName or r2Name to differentiate the two constructors... ...Which leads me to the other problem of different record constructors of the same data type having different fields, which can lead to some very ugly runtime exceptions (so much for a strong type system!): => data MyData = MyData1 { name :: String } | MyData2 { value :: Int } => let x = MyData1 { name = "hello" } => x { value = 0 } -- <-- this causes an exception because 'value' field does not exist for the constructor MyData1, but does for MyData2 so it matches the type system and there is no compile-time warning to catch it. Another big issue I've recently had with my code was dealing with multiple constructors into pattern matches/pattern guard conditionals. For example, I have a record type like this: data MyData = A { ... } | B { ... } | C { ... } and I want a case to match either A or B, otherwise do C. Let's say I have a function f1 that I want to run on A and B, and a function f2 that I want to run on C. I'm forced to do something like case myData of A{} -> f1 B{} -> f1 C{} -> f2 Whereas I'd love to be able to do case myData of A{} or B{} -> f1 C{} -> f2 This becomes a big problem when I have a lot of cases to take care of, and repetition makes it harder to spot bugs and easier to mess up.
- pka 10y agoYes, record accessors being partial functions is a big problem. One way to deal with this is to not use fields at all together with algebraic data types (i.e. when you have more than one constructor), just pattern match. When you have some common field across all constructors, define a regular function to pull it out: someData :: MyData -> a and use that instead. As to the second use case, if you need to access something common to A and B but not C, then: someOtherData :: MyData -> Maybe a and pattern match on that. Or first pattern match on C and then have a catch all _ for A and B. lens remedies a lot of Haskell's pain points when it comes to records, but I have to say that PureScripts record system is so much nicer.
- maweki 10y agoLens is really cool but sadly it depends on ghci being available which immediately breaks ARM compatibility. :(
- dllthomas 10y ago> sadly it depends on ghci being available Do you mean Template Haskell? If so, I'm pretty sure it's not required for use, just to have the lenses defined for you. You can still define them manually, at the cost of a little extra boilerplate. If you actually mean ghci, then I have no idea what you're talking about...
- nothrabannosir 10y agoThey saved the best for last: 3.5 Naming Conventions Some names are inconsistent. For example Functor is not called Mappable while Traversable is not named after abstract nonsense. It is best to focus on what things are instead of what they are called.
- wz1000 10y agoI don't really agree with the example that was chosen. "Functor" is a well defined term with a specific meaning. A much better example would be "Show"(Showable) vs "Traversable".
- tempodox 10y agoNope. In OCaml, “Functor” is a well-defined term with a completely different meaning. Or go look what “Functor” means in Prolog. Well defined, completely different. If you really want to mess things up, use names that mean something different every day. But I would know what a “Mappable” should be, regardless of PL.
- wz1000 10y agohttps://en.wikipedia.org/wiki/Functor https://en.wikipedia.org/wiki/Functor
- awj 10y ago...you're kind of helping the "don't use Functor" argument here. Most programmers are programmers first and mathematicians second, if at all. Reusing terms from math that have been overloaded in programming domains is definitely going to create a scenario where many (most?) users don't correctly understand the terms you're using.
- wz1000 10y agoA Functor is a fundamental category-theoretical term with a specific definition that simply isn't captured by something vague like "Mappable". Precision is important when talking about such terms. It is not at all clear that (-> a) is "Mappable". Lots of terms like Profunctor, Contravariant Functor, Bifunctor etc. (which are also the names of typecasses in Haskell) rely on the precise, category-theoretical definition of Functor.
- seagreen 10y ago1.6 "Monad Type Class" was fixed in GHC 7.10! Some of these complaints are goofy like the one about the naming of Functor. Others like the one about records being bad are perfectly legitimate. It would be awesome to see real improvement on this. Unnecessary runtime errors are evil.