在以下示例中:
data ColourName = White | Grey | Gray | Black | Blue -- ... -- hundreds more of colours -- ... | LastColor deriving (Read, Show, Eq)
我想重新定义(==)
,这样Grey
和Gray
评估作为平等的.
很显然,一个办法是不包括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
或类似.我希望有效颜色表示为枚举,例如提供自动验证,但希望允许模块的最终用户进行拼写变化!)
不要这样做.它不适用于模式匹配.它会破坏类似的东西
f Gray = g f x = h
因为模式匹配不关心你的Eq
实例.
休息时,我的意思是它不会有你想要的行为,因为f Grey
最终会调用h
而不是g
,即使你期望f x == f y
为所有人x == y
.这意味着程序员必须明确记住为两者制作案例,f Gray
而f Grey
这只是愚蠢的.
如果你决定让一个丑陋的黑客允许替代拼写,我想你可以做到
#define Gray Grey
启用CPP.
根据定义,值Grey
和Gray
不相等.没有任何迹象表明它们应该是平等的,除了你附加到它们的额外语义.我会说这是对Eq
类型类的滥用.
定义一个函数来处理这些额外的语义:
sameColour :: Color -> Color -> Bool sameColour Grey Gray = True sameColour Gray Grey = True sameColor a b = a == b
这可以很容易地扩展到处理多个颜色"同义词"
与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
,但是你必须比较字符串会有一点性能损失.
您可以使用派生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