Haskell数据结构的奇怪之处

4

我一直在尝试编写一个小文件来尝试使用类似于袋子的数据结构。到目前为止,我的代码如下:

data Fruit = Apple | Banana | Pear deriving (Eq, Show)
data Bag a = EmptyBag | Contents [(a, Integer)]

emptyBag :: Bag a
emptyBag = EmptyBag

unwrap :: [a] -> a
unwrap [x] = x

isObject theObject (obj, inte) = theObject == obj

count :: Bag a -> a -> Integer
count (Contents [xs]) theObject = snd (unwrap (filter (isObject theObject) [xs]))
count EmptyBag _ = 0

但是当我尝试运行它时,我遇到了错误: 无法从上下文中推导出 (Eq a) 源自于对 'isObject' 的使用 at ....

而当我将计数函数拿出来并调用 snd(unwrap(filter (isObject Banana) [(Apple,1),(Banana,2)])) 它会愉快地返回2。

有关此问题的任何线索或编写此类数据结构的建议都将不胜感激。


1
不需要一个“EmptyBag”构造函数;使用“Contents []”即可。 - augustss
当您尝试执行类似于 count (Contents [(Banana,2),(Apple,3)]) Apple 这样的操作时,由于 [xs] 仅匹配具有一个元素的列表,因此 count 也会崩溃! - yatima2975
1个回答

6

(==) 只能在包含 Eq 的上下文中使用,但是当你声明 count 时没有包含该上下文。如果我理解正确,应该是这样的:

count :: Eq a => Bag a -> a -> Integer

如果你在不包含类型的情况下声明count,你可以要求ghci推断类型;或者只需询问snd(unwrap(filter(isObject Banana)[(Apple,1),(Banana,2)]))的推断类型。


你的解决方案是正确的,但我并不完全明白为什么;我认为既然 isObject 函数适用于比较,它在这种情况下也应该可以继续使用,那么添加 Eq a => 在程序中到底有什么变化呢? - Owen
Eq a 告诉编译器 theObjectEq 类型类的成员 - 也就是说,你可以测试它是否等于同类型的其他东西。有一些数据类型(函数是最好的例子)不是 Eq 类型类的成员。(例如,(+1) 和 ((+2).(-1)) 不能被测试相等,尽管在几乎所有情况下它们将返回相同的结果。) - dvitek
秘密地,像(==)这样的类型类函数在包含有关类型信息的表中查找;Eq a =>声明了这个表的类型,它作为一个额外的参数在幕后传递。(如果您查看GHC Core输出,您将看到实际的表被传递。)使用isObject,您让GHC自己计算出类型,并且它已经计算出来,包括Eq a约束;但是当您自己指定count的类型时,则不能忽略这些(至少不使用一些相当复杂的Oleg技巧)。所有或无。 - geekosaur
去掉 count 的显式类型签名也可以解决这个问题。然后你可以使用 ghci 来查看推断出的类型。 - Dan Burton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接