Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

overriding default Eq definition for specific pattern

Tags:

haskell

Suppose I have defined some data type that derives Eq but want to insert my own definition of (==) for some pattern. Is there any way to do this or do I have to define (==) for every pattern?

e.g.

data Asdf = One Char | Two Char Char --(deriving Eq)
instance Eq Asdf where
  (==) (One _) (One _) = True
  --otherwise use what the derived definition would have done
  --can I do this without defining these patterns myself?
like image 824
Aneesh Durg Avatar asked Dec 10 '22 11:12

Aneesh Durg


2 Answers

To do what you're trying to do, you have to define it yourself, and that means you have to define it for every pattern.

Basically data MyType x = A x | B x x deriving (Eq) will add a default derivation equivalent to,

instance Eq x => Eq (MyType x) where
    A x1    == A x2     = x1 == x2
    B x1 x2 == B x3 x4  = x1 == x3 && x2 == x4
    _       == _        = False

Note that it figures out the necessary dependencies (the Eq x => part above) as well as fills in the diagonal cases -- the special cases among the n2 possible matches where the same constructor was used.

As far as I know, it does this definition all-at-once and there is no way to dig into an existing instance declaration to mess with it -- and there is a good reason for this; if they let you do this then that would mean that as codebases grow, you could not look at an instance derivation or a deriving (Eq) clause and be confident that you know exactly what it means, since some other part of the code might monkey-patch that Eq instance to do something nefarious.

So one way is to redefine the diagonal yourself. But that's not the only way. There is at least one alternative which may work if it's easier to modify several usage sites than to shove all n constructors into a single thing:

newtype EverythingIsEqual x = E x deriving (Show)
instance Eq (EverythingIsEqual x) where
    _ == _ = True

data MyType x = A (EverythingIsEqual x) | B x x deriving (Show, Eq, Ord)

This newtype allows you to strategically modify certain terms to have a different Eq relation at no runtime cost -- in fact this is pretty much one of the two central arguments for newtypes; aside from the lesser one where "I want to have a type-level difference between these two Strings but they ARE just strings and I don't want to pay any performance penalty," there is the greater argument of "sometimes we want to tell Haskell to use a different Ord dictionary without messing with any of the values that this dictionary acts upon, we just want to swap out the functions."

like image 120
CR Drost Avatar answered Jan 12 '23 16:01

CR Drost


This question discusses how to do something very similar for the Show instance, using the https://hackage.haskell.org/package/generic-deriving package: Accessing the "default show" in Haskell?

See this answer in particular: https://stackoverflow.com/a/35385768/936310

I recently used it for the Show instance recently, and it worked wonderfully. You can similarly derive Eq as well for your type, assuming it's regular enough.

like image 23
alias Avatar answered Jan 12 '23 14:01

alias