我有一组相对较大的代数数据类型,我无法自动派生Eq
,Ord
因为数据类型中的单个字段被认为是元数据,不应该考虑相等和排序。例如,数据类型可能如下所示:
data Foo = A Int | B String | C String Int | ... | Z String String Int
在这种情况下,每个 Int 都是元数据。
所以我所做Eq
的只是通过比较构造函数来手动实现。但这Ord
变得疯狂,因为如果我有n
构造函数,我必须实现n^2
比较函数。所以目前我的工作是手动实现Hashable
,这需要我为每个构造函数实现一个哈希函数。然后在我的Ord
实例中进行哈希比较。
这显然有一些问题,因为compare (hash x) (hash y) == EQ -> x == y
不成立,因为两个不同的值可以共享相同的散列。然而,这可以通过首先手动检查是否相等来处理,如果是这种情况,总是说左边比右边小。
但是,现在对于它持有的任何类型的某些值,您都拥有它a < b && b < a
。我不确定在 HaskellOrd
实例中是否允许。所以基本上我的问题是这样实施 Ord 是否可行?我需要的原因Ord
是因为许多库需要Ord
. 例如图形库和地图库。
这是一个完整的例子:
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
module Test where
import Prelude
import Data.Bits (xor)
import Data.Hashable (Hashable (..))
data Foo = A Int | B String | C String Int | Z String String Int
instance Eq Foo where
(A _) == (A _) = True
(B x1) == (B x2) = x1 == x2
(C x1 _) == (C x2 _) = x1 == x2
(Z x1 y1 _) == (Z x2 y2 _) = x1 == x2 && y1 == y2
_ == _ = False
instance Hashable Foo where
hashWithSalt s (A _) = s `xor` (hash @Int 1)
hashWithSalt s (B x) = s `xor` (hash @Int 2) `xor` (hash x)
hashWithSalt s (C x _) = s `xor` (hash @Int 3) `xor` (hash x)
hashWithSalt s (Z x y _) = s `xor` (hash @Int 4) `xor` (hash x) `xor` (hash y)
instance Ord Foo where
compare (hash -> a) (hash -> b) = case compare a b of
EQ -> if a == b then EQ else LT
e -> e