(重新) - 为等式Eq定义(==)

时间:2014-06-26 14:31:14

标签: haskell

在以下示例中:

data ColourName 
  = White
  | Grey
  | Gray
  | Black
  | Blue
  -- ...
  -- hundreds more of colours
  -- ...
  | LastColor
  deriving (Read, Show, Eq)

我想重新定义(==),以便GreyGray评估为相等

显然,一种方法是Eq中包含deriving,但是,我必须定义

(==) :: ColourName
(==) White White = True
(==) Gray Gray = True
(==) Grey Grey = True
(==) Gray Grey = True
(==) Grey Gray = True
(==) Black Black = True
-- frickin' log of other colors, hundreds of lines of typing
(==) LastColor LastColor = True
(==) a b = False

这不是我打算做的事。

我也不能

instance Eq ColourName where
    (==) :: ColourName -> ColourName -> Bool
    (==) Gray Grey = True
    (==) Grey Gray = True
    (==) a b = (a == b)

因为这会导致无限递归,基本上是未定义的。

有出路吗?

(不,我不想使用data Colour = Colour String或类似的。我希望将有效颜色表示为枚举,例如提供自动验证,但希望允许最终用户的拼写变化模块!)

5 个答案:

答案 0 :(得分:12)

您可以使用派生的Enum实例:

data ColourName = Gray | Grey | ...
  deriving (Read, Show, Enum)

instance Eq ColourName where
  Gray == Grey = True
  Grey == Gray = True
  a == b = fromEnum a == fromEnum b

编辑:您还可以将PatternSynonyms与GHC 7.8+一起使用。它的工作方式类似于智能构造函数,但也可用于模式匹配。

pattern Gray = Grey

答案 1 :(得分:9)

不要这样做。它在模式匹配方面不会很好用。它会打破像

这样的东西
f Gray = g
f x    = h

因为模式匹配不关心您的Eq实例。

休息时,我的意思是它不会有你想要的行为,因为f Grey最终会调用h而不是g,即使你期望{所有f x == f y {1}}。这意味着程序员必须明确记住为x == yf Gray创建只是愚蠢的案例。

如果你决定有一个丑陋的黑客来允许替代拼写,我想你可以做到

f Grey

启用CPP。

答案 2 :(得分:7)

根据定义,值GreyGray不相等。没有任何迹象表明它们应该是平等的,除了你附加到它们的额外语义。我会说这是对Eq类型类的滥用。

定义一个函数来处理这些额外的语义:

sameColour :: Color -> Color -> Bool
sameColour Grey Gray = True
sameColour Gray Grey = True
sameColor  a    b    = a == b

这可以很容易地扩展到处理多种颜色的“同义词”

答案 3 :(得分:6)

与Piezoid的答案类似,使用Show实例进行比较可能会降低效率:

data ColourName = Gray | Grey | ...
    deriving (Show, Read)

instance Eq ColourName where
    Gray == Grey = True
    Grey == Gray = True
    a == b = show a == show b

然后你不必依赖于使用Enum,但是你必须比较字符串会有一点性能损失。

答案 4 :(得分:4)

我会在这里使用newtype

newtype ColourNameEquatingGrayAndGrey = CNEGAG ColourName
instance Eq ColourNameEquatingGrayAndGrey where
    CNEGAG Gray == CNEGAG Grey = True
    CNEGAG Grey == CNEGAG Gray = True
    CNEGAG a    == CNEGAG b    = a == b

(抱歉愚蠢的类型和构造函数名称......)

这使您可以保留deriving Eq,这使您非常明确地将代码中的不同拼写放在一起,并且您仍然可以使用nub等库函数(相比较)必须切换到nubBy sameColour(如@ cdk' s)或类似的东西)。如果需要,您还可以创建自己的Show实例,并且运行时成本应该最小。

我现在唯一能想到的缺点是模式匹配变得更加繁琐,但我猜测有100多种替代方案并不是你做的事情!