2

我需要为数据派生Eq,但对于某些构造函数,我想忽略一些字段。数据用于表示 DataTypes(我们正在开发一个编译器):

data DataType
    = Int | Float | Bool | Char | Range | Type
    | String Width
    | Record (Lexeme Identifier) (Seq Field) Width
    | Union  (Lexeme Identifier) (Seq Field) Width
    | Array   (Lexeme DataType) (Lexeme Expression) Width
    | UserDef (Lexeme Identifier)
    | Void | TypeError
    deriving (Ord)

我需要从它出现的每个构造函数中忽略该Width 字段

4

3 回答 3

8

如果您希望使用自定义 Eq 语义,则无法派生 Eq。您必须手动编写一个实例。

一个常见的技巧是:

  • 定义一个删除您希望忽略的字段的 DataType'
  • 为此导出 Eq
  • 将 DataType 的 Eq 定义为a == b = toDataType' a == toDataType' b

这至少使它不那么特别,以它自己的类型捕获不同的 Eq 语义,它/可以/被派生。

于 2014-05-29T10:53:10.537 回答
7

Don 的另一种方法是使用包装器类型来编码您想要的特殊字段的实例:

newtype Metadata a = Metadata { unMetadata :: a }

instance Eq (Metadata a) where
    (==) _ _ = True

instance Ord (Metadata a) where
    compare _ _ = EQ

然后,您可以将 DataType 定义中的所有Width'替换为Metadata Width并派生实例。

data DataType
    = Int | Float | Bool | Char | Range | Type
    | String (Metadata Width)
    | Record (Lexeme Identifier) (Seq Field) (Metadata Width)
    | Union  (Lexeme Identifier) (Seq Field) (Metadata Width)
    | Array   (Lexeme DataType) (Lexeme Expression) (Metadata Width)
    | UserDef (Lexeme Identifier)
    | Void | TypeError
    deriving (Eq, Ord)

此解决方案使您的 DataType 定义更加冗长(并且更明确?),但在使用Width值时需要包装和展开。

于 2014-05-29T11:48:58.873 回答
1

您可以编写自己的实例Eq

instance Eq DataType where
   Int   == Int   = True
   Float == Float = True 
   Bool  == Bool  = True
   Char  == Char  = True
   Range == Range = True
   Type  == Type  = True
   (String _) == (String _) = True
   (Record l1 s1 _)  == (Record l2 s2 _)  = (l1 == l2) && (s1 == s2)
   (Union  l1 s1 _)  == (Union  l2 s2 _)  = (l1 == l2) && (s1 == s2)
   (Array   l1 e1 _) == (Array   l1 e1 _) = (l1 == l2) && (e1 == e2)
   (UserDef i1)      == (UserDef i2)      = i1 == i2
   Void      == Void      = True
   TypeError == TypeError = True
   _ == _     = False
于 2014-05-29T12:22:14.137 回答