我想分层次地定义数据类型,例如:
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不会承认一只大猫或小猫 是动物?
答案 0 :(得分:18)
您将类型与其构造函数混合在一起。类型是您可以创建变量的类型。类型构造函数是用于创建此类数据的构造函数。在您的代码中,data Animal = Cat | Dog
使用两个构造函数 Animal
和Cat
声明类型 Dog
。在另一行中,您定义数据类型Cat
。这没有问题,因为类型和构造函数不共享相同的命名空间。
如果您想在Cat
中嵌入Animal
类型的对象(如果使用了构造函数Cat
),则可以向构造函数添加一个字段:
data Animal = Cat Cat | Dog
这意味着:“Animal
是一种包含两个构造函数的类型Cat
和Dog
。Cat
的字段类型为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
Ord
,Enum
和& c。比特殊用途的东西更好。如果意图是一种更开放的方式来建模具有各种属性的实体,那么值得考虑更适合实际问题的其他设计。