我在为数据类型创建自己的Eq实例时遇到了一些问题。
这是我的代码:
data Doc = Empty -- Adds ""
| Text String
| NewLine -- Adds "\n"
| Concat Doc Doc -- Joins two Doc's
instance (Eq Doc) => Eq (Doc) where
Concat a1 b1 == Concat a2 b2 = True
_ == _ = False
我的目标是,当最终的Doc相同时,它只返回True
,例如:
*Main> Concat (Concat (Text "The ") (Text "birds"))(Concat Empty NewLine) == Concat Empty (Text "The birds\n")
这应该是True
并且我拥有的代码确实返回True,但它每次都返回True,即使我的句子完全不同。
我一直在争吵这一段时间并四处搜寻,但却什么也没找到。你们中的任何人对我在这里做错了什么都有任何想法或建议吗?
提前致谢!
答案 0 :(得分:4)
这里有两个问题,让我们轮流看看。
我的定义有什么问题?
您可以将任意两个Concat
文档定义为相同,无论它们包含什么。您还可以将任何其他值定义为inequal,无论它们包含什么。这意味着,例如,
Concat (Text "foo") Newline == Concat (Text "bar") Newline
给出True
,而
Text "foo" == Text "foo"
给出False
。
让我们为结构相等编写一个定义:如果它们具有相同的结构,则两个Doc
是相等的:
instance Eq Doc where
-- Concats are equal if the things they are concat-ing are equal
Concat a b == Concat a' b' = a == a' && b == b'
-- Newlines are always equal
NewLine = NewLine = True
-- Two texts are equal if the text they contain is equal
Text a = Text a' = a == a'
-- Everything else is inequal
_ = _ = False
我怎样才能编写一个不关心结构的Eq实例,只知道生成的文档是否相同?
如果我们想比较生成的文档,我们可以这样做:
instance Eq Doc where
doc == doc' = generate doc == generate doc'
假设generate :: Doc -> String
完成了它的说法。我们还可以使用on
中的Data.Function
将其写为
(==) == (==) `on` generate
正如威廉在评论中提到的,这“不满足平等约束”。但是,有一个先例:diagrams包认为两个图是相同的,如果它们生成相同的绘制图表的指令。在the paper introducing Diagrams:
中探讨了这背后的原因特别细心的读者可能已经注意到了一些事情 关于这个Semigroup实例很奇怪:(<>)不是关联的! d1<> (d2< d3)和(d1< d2)< d2>。 d3不相等,因为它们的结果 在两种不同形状的树木。然而,直观地看来 d1<> (d2< d3)和(d1< d2)< d2>。 d3仍然“道德”相同,即 它们是“相同”图表的两种表示。我们可以正式化 这个想法通过考虑图表作为商类型,使用一些 结构平等以外的等价关系。特别是,相关性 如果我们考虑两个图表d1和d2等效,则确实成立 每当unDd1≡unDd2,其中unD :: Diagram→[Prim] 将一个图表“编译”成一个原始的平面列表。
答案 1 :(得分:1)
根据您的代码,任何Concat
都将等于另一个Concat
。让我们尝试手工检查相等性:
Concat NewLine (Text "Huh") == Concat (Text "Example") (Text "Concat")
= True -- inserted definition of `==`
我们从不关注Concat
的元素。但在某些时候,我们必须检查Newline == Text "Example"
并返回False
。一个重要提示是,您永远不会检查a1
,b1
等。一旦我们尝试使用它们,实例就会自然而然地出现:
instance Eq Doc where
Concat a1 b1 == Concat a2 b2 = a1 == a2 && b1 == b2
Text ... == ... = -- left as exercise
Newline ... == ... = -- left as exercise
_ == _ = False