haskell中的通用实体列表

时间:2014-06-06 20:28:33

标签: haskell

我有一个实体列表,比方说[Entity]。每个Entity具有不同的功能,例如一些可以绘制到屏幕,而其他可以发出声音。所有这些都可以打印出来以获得调试信息。

鉴于此,我们可能会有三个类型类:Show,Draw&声音。一个实体可能是Show和Draw的实例,而另一个实体可能是Show和Sound。

我试图找到Entity的类型,给定这些约束,并且考虑到我想按功能遍历列表,例如:找到可以显示的所有实体,或者所有能播放声音的实体。

到目前为止,我无法用haskell的类型系统来表达这一点,唯一的方法似乎是使用运行时检查,基本上是实现我自己的约束系统。

有什么想法吗?

2 个答案:

答案 0 :(得分:5)

如果您要以不同于构造和使用Entity的方式使用类型类,我建议使用这些类型类进行建模 - 否则会浪费时间和代码行。请参阅the existential typeclass (anti-)pattern

您描述Entity的方式是“功能”的集合。包含要素字段的记录是对此进行建模的常用且简洁的方法。对于可选功能,请使用Maybe

data Entity = Entity {
    image :: Maybe Image,
    sound :: Maybe Sound,
    debug :: String
}
instance Show Entity where show = debug

(我使用的是语义上有意义的名称,例如ImageSound,如果你想对这些名称进行必要的考虑,你可以替换IO ()。)

请记住,您可以在这里使用HOF,因此功能特性是可以接受的:

    converse :: Maybe (Question -> Answer)

通常用于此问题的模式是,不是使用Entity { ... }创建实体,而是让它们专门化为默认值:

defaultEntity :: Entity
defaultEntity = Entity {
    image = Nothing,
    sound = Nothing,
    debug = "<Entity>"
}

然后定义如下的实体:

invisibleDog :: Entity
invisibleDog = defaultEntity {
    debug = "<Invisible Dog>",
    sound = Just (Sound.resource "woof.wav")
}

现在,当您添加新功能时,您只需要更新defaultEntity,所有其他实体都将继承默认值。

答案 1 :(得分:3)

尝试在类型级别表达Entity之间的差异,您将没有太多运气。至少,如果你想将它们存储在同质[]中,那就不是了。您立即受到限制,Entity的所有值必须按类型无法区分。

我认为类型类或存在性类型不是必需的。我建议如下:

data Trait = Showable String | Drawable Image | Playable Sound

根据您计划绘制图像/播放声音的方式定义ImageSound。然后Entity是这些特征的简单可组合集合:

newtype Entity = Entity (Set Trait)

您可以轻松地为Monoid提供Entity个实例,以允许Trait的组合。然后,您可以“按功能遍历列表”,如下所示:

showables :: [Entity] -> [Entity]
showables = filter $ \(Entity es) -> any isShowable (Set.toList es)
    where isShowable (Showable _) = True
          isShowable _ = False

具有DrawablePlayable的相似功能。

此解决方案是否满足您的所有标准?

编辑: luqui指出了这个模型的一些缺陷。可以使用手动EqOrd个实例来避免这些情况。

instance Eq Trait where
    Showable _ == Showable _ = True
    Drawable _ == Drawable _ = True
    Playable _ == Playable _ = True
    _ == _ = False

instance Ord Trait where
   Showable _ <= _ = True
   _ <= Playable _ = True
   _ <= _ = False

现在Trait中无法复制Set并提供可行的Ord实例。这有点拉伸,可能不适合您的用例。