Use parts of constructor for deriving instance in Haskell data

413 Views Asked by At

I need to derive Eq for a data, but for some constructors I want to ignore some fields. The data is for representing DataTypes (we are developing a compiler):

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)

I need to ignore the Width field from every contstructor it appears in.

3

There are 3 best solutions below

1
On BEST ANSWER

You could write your own instance of 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
0
On

You cannot derive Eq if you wish to use custom Eq semantics. You must write an instance by hand.

A common trick is to:

  • define a DataType' that drops the fields you wish to ignore
  • derive Eq for this
  • define Eq for DataType as a == b = toDataType' a == toDataType' b

This at least makes it less ad hoc, capturing the different Eq semantics in its own type, where it /can/ be derived.

1
On

Another approach from Don's is to use a wrapper type to encode the instances you want for the special fields:

newtype Metadata a = Metadata { unMetadata :: a }

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

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

You can then replace all the Width's in your DataType definition with Metadata Width and derive the instances.

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)

This solution makes your DataType definition a bit more verbose (and more explicit?) but requires wrapping and unwrapping when using the Width values.