分层数据类型

时间:2011-12-02 17:13:02

标签: haskell

我想分层次地定义数据类型,例如:

data Cat = BigCat | SmallCat
data Animal = Cat | Dog

然后编写一个将Animals作为参数的函数,并使用如下模式匹配来编写它:

bigger::Animal -> Animal -> Bool
bigger SmallCat BigCat = False
bigger BigCat SmallCat = True
bigger Dog Cat = True
bigger Cat Dog = False

编译器抱怨。它不希望将函数签名中明确写入的类型Animal与模式匹配的第一行和第二行中的类型Cat进行匹配。为什么haskell不会承认一只大猫或小猫 是动物?

2 个答案:

答案 0 :(得分:18)

您将类型与其构造函数混合在一起。类型是您可以创建变量的类型。类型构造函数是用于创建此类数据的构造函数。在您的代码中,data Animal = Cat | Dog使用两个构造函数 AnimalCat声明类型 Dog。在另一行中,您定义数据类型Cat。这没有问题,因为类型和构造函数不共享相同的命名空间。

如果您想在Cat中嵌入Animal类型的对象(如果使用了构造函数Cat),则可以向构造函数添加一个字段:

data Animal = Cat Cat | Dog

这意味着:Animal是一种包含两个构造函数的类型CatDogCat的字段类型为Cat并且Dog没有。“如果要使用构造函数Cat创建对象,则必须将Cat类型的对象传递给它:

myCat = Cat BigCat

如果要匹配Animal,则必须列出匹配构造函数的所有字段。比较代码的更正版本:

data Cat = BigCat | SmallCat
data Animal = Cat Cat | Dog

bigger :: Animal -> Animal -> Bool
bigger (Cat SmallCat) (Cat BigCat)   = False
bigger (Cat BigCat)   (Cat SmallCat) = True
bigger Dog            (Cat _)        = True
bigger (Cat _)         Dog           = False

_表示不关心 - 无论传递的对象如何,这都将始终匹配。

答案 1 :(得分:12)

此处的即时错误是Animal定义了两个与Cat无关的数据构造函数:表达式Cat的类型为Animal,而表达式BigCat的类型为Cat

要以简单的方式创建嵌套数据类型,您需要使Cat类型成为相关构造函数的参数:

data Cat = BigCat | SmallCat
data Animal = Cat Cat | Dog

然后你可以这样做:

bigger (Cat SmallCat) (Cat BigCat) = False
bigger (Cat BigCat) (Cat SmallCat) = True
bigger Dog (Cat _) = True
bigger (Cat _) Dog = False

如果超出一个简单的例子,这变得非常笨拙,但Cat类型的冗余是痛苦的,标识符Cat的两种不同用法是不必要的混淆。略微改进是避免将尺寸与物种混为一谈,而是采取以下措施:

data Size = Big | Small
data Species = Cat | Dog
data Animal = Animal Species Size

这里的一个优点是您可以更轻松地扩展任何类型,而无需添加尽可能多的样板废话。

然而,除了玩具示例之外,这两者都非常愚蠢,在实际使用中,很可能是一种更好的方法。如果类型真的是简单的枚举比猫和狗更有意义,那么deriving OrdEnum和& c。比特殊用途的东西更好。如果意图是一种更开放的方式来建模具有各种属性的实体,那么值得考虑更适合实际问题的其他设计。