数据类型的自定义Eq实例

时间:2018-01-21 18:15:44

标签: haskell

我在为数据类型创建自己的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,即使我的句子完全不同。

我一直在争吵这一段时间并四处搜寻,但却什么也没找到。你们中的任何人对我在这里做错了什么都有任何想法或建议吗?

提前致谢!

2 个答案:

答案 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。一个重要提示是,您永远不会检查a1b1等。一旦我们尝试使用它们,实例就会自然而然地出现:

instance Eq Doc where
  Concat a1 b1 == Concat a2 b2 = a1 == a2 && b1 == b2
  Text ...     == ...          = -- left as exercise
  Newline ...  == ...          = -- left as exercise
  _            == _            = False