我有数据类型
data MyType = Any | A | B | C ...
和" Any"的语义是它应该等同于所有其他情况。我认为最优雅的方法是实现我自己的Eq实例,从
开始instance Eq MyType where
Any == _ = True
_ == Any = True
但是现在我还没有找到避免重复和愚蠢的代码的方法,比如
A == A = True
B == B = True
...
我虽然"滥用" show函数,只做x == y = (show x) == (show y)
,但有更清洁的方法吗?
答案 0 :(得分:13)
你也许可以这样做:
data Wildcard a = Any | Card a
data NewType = A | B | C | ... deriving Eq
type OldType = Wildcard NewType
instance (Eq a) => Eq (Wildcard a) where
Any == _ = True
_ == Any = True
Card l == Card r = l == r
这样,编译器会自动为您导出Eq OldType
,并且Eq NewType
具有预期的语义。 (我们可以将Any
应用于我们想要的任何类型......)
答案 1 :(得分:11)
您已经获得了一些解释如何执行此操作的好答案。现在我要解释为什么你绝对不应该这样做。
Haskell中的一个类不仅仅是一堆方法。它还附带法律。这些法则对于允许人们编写有意义的类多态函数至关重要。不幸的是,Prelude
没有记录Eq
类的任何法律。原因是人们希望使用==
和/=
来实现浮点表示的便利性,浮点表示通常是“平等”的概念。不礼貌。我认为这是哈斯克尔委员会的一个严重错误。
那么Eq
实例应遵守哪些法律?一个明显的问题涉及其方法:
a /= b = not (a == b)
其他重要的,普遍接受的法律是==
描述equivalence relation所必需的法律。特别是,对于所有a
,b
和c
,
反身法:a == a = True
(见脚注)
对称法则:a == b = b == a
传递法:如果a == b && b == c = True
,则a == c = True
还有(近似)替代性的一般假设。如果f
是典型函数(不是一个奇怪的抽象破坏内部函数或调试函数),a == b = True
,那么f a == f b = True
。结合其他法则,这意味着==
反映了类型所代表的基础模型的数学相等性。
您对==
的定义违反了传递性。特别是,
A == Any && Any == B = True
但
A == B = False
这意味着如果您将类型与Eq
实例上的任何函数一起使用多态,则必须阅读该函数的源代码以确定它是否会按您希望的方式运行。如果该函数的实现发生了变化,您将不得不再次阅读其源代码,以检查它是否仍然可以执行您想要的操作。在实践中,这非常糟糕。
你如何解决这个问题?不要编写一个奇怪的Eq
实例,而是编写自己的比较卡片的函数!
data Wildcard a = Any | Card a
data NonWild = A | B | C | ... deriving Eq
matching :: Eq a => Wildcard a -> Wildcard a -> Bool
matching (Card a) (Card b) = a == b
matching _ _ = True
Haskell中的值可能是无限的或未完全定义的。因此,反身法可能不是严格正确的。例如,repeat 1 == repeat 1
永远不会终止(它表示"底部"值)。因此,在实践中,适当的反身法律规定a == a
不是False
。一般认为==
在其参数有限且定义明确时将终止。
答案 2 :(得分:6)
您还可以滥用其他类,例如Enum
:
data MyType = Any | A | B | C ...
deriving Enum
instance Eq MyType where
Any == _ = True
_ == Any = True
a == b = fromEnum a == fromEnum b
但是最好的'这种方法最终取决于您的实际用例。如果你只是有一些案例,我建议你手工写出来;有一个案例可以避免重复的代码,但不要过度使用,以至于你拒绝完全编写代码。
答案 3 :(得分:3)
您可以派生Enum并使用它来定义Eq类
data T = Any | A | B | C deriving (Enum, Show)
instance Eq T where
a == b = fromEnum a == fromEnum b || fromEnum a == 0 || fromEnum b == 0