说我有以下数据:
data A
= B Int
| C Float
| D A
如果我想派生Eq
,但更改输出,以便比较两个与构造函数D
的项始终相等,是否有一种方法可以对所有其他构造函数都实现而不执行呢?对于其他情况,我想导出默认的Eq
实现。
我想实现的目标是
instance Eq A where
(D _) == (D _) = True
_ == _ = undefined -- Use default eq
答案 0 :(得分:2)
没有直接的方法将GHC生成的默认Eq A
实例合并到您自己的Eq A
实例中。问题在于,实例代码的生成与定义这些实例的过程有关-生成默认Eq A
代码的唯一方法是实际生成唯一的Eq A
实例,然后生成一次实例已生成,您无法真正更改它。我什至没有办法解决GHC“派生”相关的扩展。
但是,您可以使用由软件包Eq
提供的默认generic-deriving
实例的重新实现。带有一些序言:
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
import Generics.Deriving.Eq -- from package generic-deriving
使用派生的Generic
实例定义数据类型:
data A
= B Int
| C Float
| D A
deriving (Generic)
然后,定义一个GEq
实例,该实例为D
构造函数实现您的特殊情况,同时将其余实例的默认实现推迟。
instance GEq A where
D _ `geq` D _ = True
x `geq` y = x `geqdefault` y
最后,定义一个Eq
实例,并使用此泛型相等类。
instance Eq A where
(==) = geq
在那之后,它应该都能按预期工作:
> D (B 10) == D (B 20)
True
> B 10 == B 20
False
>
但是,在注释中采纳建议可能更合理,并且可以:
执行@malloy的建议。您尝试定义的操作实际上不是(==)
,所以为什么需要将其命名为(==)
?只需派生通常的Eq
实例并编写一个单独的函数即可避免不必要的递归:
equalClasses :: A -> A -> Bool
equalClasses (D _) (D _) = True
equalClasses x y = x == y
如果您真的想使用(==)
,我认为使用@luqui建议的newtype
可能是最惯用的方法:
data A'
= B Int
| C Float
| D A'
deriving (Eq)
newtype A = A A'
instance Eq A where
A (D _) == A (D _) = True
A x == A y = x == y