Definitive guide on when to use Haskell typeclasses?
functional-programming, haskell, typeclass, types
Solution
Most Haskell programmers frown on needless typeclasses. These hurt type inference; you can't even make a list of `Unit`s without tricks; in GHC, there's all the secret dictionary passing; they somehow make the Haddocks harder to read; they can lead to brittle hierarchies ... maybe others can give you further reasons. I guess a good rule would be to use them when it's much more painful to avoid them. For instance, without `Eq`, you'd have to manually pass around the functions to compare, say, two `[[[Int]]]`s (or use some ad-hoc runtime tests), which is one of the pain points of ML programming.
Take a look at this blog post. Your first method of using a sum type is OK, but if you want to allow users to mod the game with new units or whatever, I'd suggest something like
data Unit = Unit { name :: String, maxHealth :: Int }
scout, tank :: Unit
scout = Unit { name = "scout", maxHealth = 10 }
tank = Unit { name = "tank", maxHealth = 20 }
allUnits = [ scout
, tank
, Unit { name = "another unit", maxHealth = 5 }
]
In your example, you need to encode somewhere that a tank has armor but a scout doesn't. The obvious possibility is to augment the Unit type with extra information like a `Maybe Armor` field or a list of special powers ... there's not necessarily a definitive way.
One heavyweight solution, probably overkill, is to use a library like Vinyl that provides extensible records, giving you a form of subtyping.
Problem
So, I understand algebraic types and type classes very well, but I'm interested in the software-engineering/best practices side of it. What is the modern consensus, if any, on typeclasses? Are they evil? Are they handy? Should they be used, and when? Here's my case-study. I'm writing an RTS-style game, and I have different kinds of "units" (tank, scout, etc.). Say I want to get the max health of each unit. My two thoughts on how to define their types are as follows: Different constructors of an ADT: ``` data Unit = Scout ... | Tank ... maxHealth :: Unit -> Int maxHealth Scout = 10 maxHealth Tank = 20 ``` Typeclass for Unit, each kind is an instance ``` class Unit a where maxHealth :: a -> Int instance Unit Scout where maxHealth scout = 10 instance Unit Tank where maxHealth tank = 20 ``` Obviously, there is going to be many more fields and functions in the final product. (For example, each unit will have a different position, etc. so not all of the functions will be constant). The trick is, there might be some functions that make sense for some units, but not others. For example, every unit will have a getPosition function, but a tank might have a getArmour function, which doesn't make sense for a scout without armour. Which is the "generally accepted" way to write this if I want other Haskellers to be able to understand and follow my code?