Haskell类型检查

时间:2012-02-05 16:11:09

标签: haskell metaprogramming

我想使用haskell来实现游戏,并且希望使用类型系统来实现项目系统。它可以这样工作:

data Wood = Wood Int

instance Item Wood where
  image a = "wood.png"
  displayName a = "Wood"

instance Flammable Wood where
  burn (Wood health) | health' <= 0 = Ash
                     | otherwise    = Wood health'
      where health' = health - 100

其中Item和Flammable类是这样的:

class Item a where
  image :: a -> String
  displayName :: a -> String

class Flammable a where
  burn :: (Item b) => a -> b

为此,我需要一种方法来检测值是否是类型类的实例。

Data.Data模块提供了类似的功能,这使我相信这是可能的。

2 个答案:

答案 0 :(得分:8)

类型类可能是错误的方法。请考虑使用普通代数数据类型,例如:

data Item = Wood Int | Ash

image Wood = "wood.png"
image Ash = ...

displayName Wood = "Wood"
displayName Ash = "Ash"

burn :: Item -> Maybe Item
burn (Wood health) | health' <= 0 = Just Ash
                   | otherwise    = Just (Wood health')
  where health' = health - 100
burn _ = Nothing -- Not flammable

如果这样做太难添加新项目,您可以改为对数据类型本身的操作进行编码。

data Item = Item { image :: String, displayName :: String, burn :: Maybe Item }

ash :: Item
ash = Item { image = "...", displayName = "Ash", burn :: Nothing }

wood :: Int -> Item
wood health = Item { image = "wood.png", displayName = "Wood", burn = Just burned }
    where burned | health' <= 0 = ash
                 | otherwise    = wood health'
          health' = health - 100

然而,这使得添加新功能变得更加困难。同时执行这两个操作的问题称为expression problem。有一个nice lecture on Channel 9 by Dr. Ralf Lämmel,他更深入地解释了这个问题并讨论了各种非解决方案,如果你有时间,值得观看。

有解决问题的方法,但它们比我说明的两个设计复杂得多,所以如果符合你的需要,我建议使用其中一个,除非必须,否则不要担心表达问题。

答案 1 :(得分:5)

问题在于:

burn :: (Item b) => a -> b

这意味着burn的结果值必须是多态。它必须能够为任何 Item实例填充任何孔。

现在,很明显你正在尝试编写类似这样的东西(用虚构的OO语言编写接口和子类):

Interface Item {
  String getImage();
  String getDisplayName();
}

Interface Flammable {
  Item burn();
}

在这种代码中,您说burn将生成某些项,而不保证项目的类型。这是“为所有人”和“存在”之间的区别。您想在Haskell代码中表达的是“存在”,但您实际表达的是“为所有人”。

现在,如果您确实确定要执行“存在”功能,则可以查看使用Existential Types。但要注意。如果你打算编写这样的代码:

if (foo instanceof Flammable) {
  ...
}

然后你几乎肯定做错了,会遇到很多痛苦和痛苦。而是考虑一下hammar建议的替代方案。