Reputation: 2196
Given a typeclass:
class AnimalTrainer animal food where
getFood :: animal -> (food, Int) -- Returns the food and the quantity
feed :: animal -> (food, Int) -- Returns the leftovers
feed a = feed' (getFood a) -- Provide a default implementation
where feed' (f, n) = (f, n - 1)
And an instance:
data Animal = Dog | Cat
data Food = Meat | Milk
instance AnimalTrainer Animal Food where
getFood Dog = (Meat, 2)
getFood Cat = (Milk, 3)
How can I write another function (somewhere else) that calls the feed function defined in the typeclass? Example:
feedEverything :: Bool
feedEverything = snd (feed Dog) == 0
Thanks
Upvotes: 0
Views: 269
Reputation: 213338
The problem is that Haskell can't figure out what type you want to use for food. It sees one instance:
instance AnimalTrainer Animal Food
But maybe there is a second instance somewhere...
instance AnimalTrainer Animal Poison
So you need to tell Haskell that animals get food only, and not something else like poison.
Solution 1: You can use functional dependencies:
class AnimalTrainer animal food | animal -> food where
...
This tells Haskell that for each animal type, there is only one food type that it will eat.
Solution 2: You can also use type families.
class AnimalTrainer animal where
type AnimalFood animal :: *
getFood :: animal -> (AnimalFood animal, Int)
feed :: animal -> (AnimalFood animal, Int)
feed a = feed' (getFood a) -- Provide a default implementation
where feed' (f, n) = (f, n - 1)
data Animal = Dog | Cat
data Food = Meat | Milk
instance AnimalTrainer Animal where
type AnimalFood Animal = Food
getFood Dog = (Meat, 2)
getFood Cat = (Milk, 3)
I personally consider this solution a little more esoteric, and the syntax a little less natural. But the example is contrived, so I include this for completeness.
Solution 3: You could add explicit type annotations whenever you call feed.
feedEverything = snd ((feed :: Animal -> (Food, Int)) Dog) == 0
Upvotes: 10