Haskell - 调用类型类中定义的函数

gab*_*ldi 0 haskell

给出一个类型类:

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)
Run Code Online (Sandbox Code Playgroud)

一个例子:

data Animal = Dog | Cat
data Food = Meat | Milk

instance AnimalTrainer Animal Food where
    getFood Dog = (Meat, 2)
    getFood Cat = (Milk, 3)
Run Code Online (Sandbox Code Playgroud)

如何编写调用类型类中定义的feed函数的另一个函数(在其他地方)?例:

feedEverything :: Bool
feedEverything = snd (feed Dog) == 0
Run Code Online (Sandbox Code Playgroud)

谢谢

Die*_*Epp 10

问题是Haskell无法确定您想要用于食物的类型.它看到一个实例:

instance AnimalTrainer Animal Food
Run Code Online (Sandbox Code Playgroud)

但也许在某个地方有第二个实例......

instance AnimalTrainer Animal Poison
Run Code Online (Sandbox Code Playgroud)

所以你需要告诉Haskell,动物只能获得食物,而不是像毒药那样的东西.

解决方案1:您可以使用功能依赖项:

class AnimalTrainer animal food | animal -> food where
    ...
Run Code Online (Sandbox Code Playgroud)

这告诉Haskell,对于每种动物类型,它只会吃一种食物类型.

解决方案2:您还可以使用类型系列.

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)
Run Code Online (Sandbox Code Playgroud)

我个人认为这个解决方案更加深奥,语法不那么自然.但这个例子是人为的,所以我把它包括在内是为了完整性.

解决方案3:您可以在调用feed时添加显式类型注释.

feedEverything = snd ((feed :: Animal -> (Food, Int)) Dog) == 0
Run Code Online (Sandbox Code Playgroud)