【问题标题】:Use parts of constructor for deriving instance in Haskell data使用构造函数的一部分在 Haskell 数据中派生实例
【发布时间】:2014-05-29 10:49:10
【问题描述】:

我需要为数据派生 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 字段

【问题讨论】:

  • 到目前为止你尝试过什么?顺便说一句,您将自动派生Ord,这意味着它将按Width 订购。所以你可能会发现自己有两个DataTypeab,所以a>ba==b 都是正确的——这似乎不是一个好主意。

标签: haskell compiler-construction derived-instances


【解决方案1】:

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

一个常见的技巧是:

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

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

【讨论】:

    【解决方案2】:

    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 值时需要包装和展开。

    【讨论】:

    • 这有一个不错的戒指:-)
    【解决方案3】:

    您可以编写自己的 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
    

    【讨论】:

    • 对 Ord 实例这样做会痛苦
    猜你喜欢
    • 2020-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-16
    • 1970-01-01
    • 1970-01-01
    • 2021-11-11
    相关资源
    最近更新 更多