
On Fri, Nov 04, 2016 at 02:06:18PM +0000, Nicholls, Mark wrote:
Subject: Re: [Haskell-cafe] programming style...and type classes...
My rule of thumb is that if there is one obvious instance (or none) for every given type, then your abstraction is a good candidate for a typeclass; if multiple instances make sense, then it's a judgment call; and if many instances could arbitrarily make sense or not depending on the usage / circumstances, then it's probably not a good candidate for a typeclass.
OK, that’s a bit more prescriptive...so if many instances make sense (Ord a) then what?
Seems like Ord is a "bad" typeclass?...it should really be a dictionary data type? And then you simply define which order you want?
Ord is a "bad" typeclass if you interpret it as "the ordering rules for this type"; but it is OK if you interpret it as "the default ordering rules for this type". Which, arguably, makes more sense for some types (Int) and less for others (Text). However, in the case of Ord, there is little harm in defining a default instance and using more specialized sorting functions that take an explicit ordering functions; this stuff is simple enough for these things to not be very intrusive at all, and giving arguable cases the benefit of the doubt, I guess, is considered pragmatic.
There needs to be 1 (or 0) instances because this is all resolved compile time?....i.e. so we (the programmer/compiler) need a function from types, and function name into instances...if there 0 instance...then boom...not defined..so you need to do something, that’s fixable....if 2...then boom...you're in trouble?
Sort of, yes. You can't have two instances of the same typeclass for the same type. However, because instances are always global, and can be defined separately from both the type and the typeclass, it is possible to write instances which, when imported, break the code that imports them by introducing conflicting instances. This is why the recommendation is to always define instances either in the module that defines the type, or the module that defines the typeclass. Instances that are defined separately from both are called "orphan instances", and people generally avoid them where possible, but occasionally practical concerns make them necessary. The most common reason for orphan instances is to avoid dependencies: suppose you write a library that defines an alternative string type; and someone else writes a library that defines useful typeclasses for string-like types. Ideally, you want your custom string type to have instances for those typeclasses, but you can't put them in the typeclass library (because you don't control it), and you don't want to put them in your own library, because then your own library has to depend on the typeclass library even though most people won't need that functionality. In such a situation, you'd bite the bullet and provide an extra library that contains just the instances, peppered with warnings about orphan instances. Another example is when you write a program that uses types from some third-party library, and wants to marshal them to and from JSON - for that to work, you need to implement ToJSON and FromJSON instances for those types, but since you control neither the type nor the JSON typeclasses, the only choice you have is to make orphan instances in your program. In that case, the damage is limited, because you won't expose the orphan instances to the outside world, so it's sort of acceptable usually.