2

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.

chamini2
  • 2,820
  • 2
  • 24
  • 37
  • 1
    What have you tried so far? Any btw, you're deriving `Ord` automatically which means it'll order by `Width`. So you might find yourself with two `DataType` values `a` and `b` so that `a>b` and `a==b` are both true - doesn't seem like a good idea. – Benesh May 29 '14 at 10:50

3 Answers3

8

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.

J. Abrahamson
  • 72,246
  • 9
  • 135
  • 180
Don Stewart
  • 137,316
  • 36
  • 365
  • 468
7

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.

Reite
  • 1,677
  • 10
  • 12
1

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
viorior
  • 1,783
  • 11
  • 16