I have defined a datatype for which I allow GHC to automatically derive an instance of the Eq typeclass.
However, the derived instance doesn't have the precise behavior I need for all cases.
For instance, see this datatype:
data IntegerOrFloat = I Integer | F Float
deriving Eq
(I 0) == (F 0) evaluates to False. I would like it to evaluate to true. If I were writing the implementation I could simply do:
instance Eq IntegerOrFloat where
(I i) == (F f) = (fromIntegral i) == f
However, I would then have to write the other three cases. Obviously this would be trivial for this type, but this is a contrived example. I very much prefer the convenience of leaving most cases automatically derived.
Is there a way for me to "override" the derived implementation for a specific case, without having to write the entire implementation by hand?
You can make use of the generic-deriving package [Hackage], this makes implementations for types that are a member of the Generic type class:
{-# LANGUAGE DeriveGeneric #-}
import Generics.Deriving.Base(Generic)
import Generics.Deriving.Eq(GEq, geq)
data IntegerOrFloat = I Integer | F Float
deriving (Generic)
instance GEq IntegerOrFloat
instance Eq IntegerOrFloat where
F f == I i = fromIntegral i == f
I i == F f = fromIntegral i == f
x == y = geq x y
geq :: GEq a => a -> a -> Bool is thus an instance that automatically generate an (==) function like Haskell does this, we can then use geq this as a base function.
You could leave the derived type as-is, but export only a newtype-wrapped version that has the special case overridden:
data IntegerOrFloat' = I' Integer | F' Float
deriving Eq
newtype IntegerOrFloat = IOF { getIOF :: IntegerOrFloat' }
instance Eq IntegerOrFloat where
IOF (I i) == IOF (F f) = fromIntegral i == f
IOF (F f) == IOF (I i) = f == fromIntegral i
IOF x == IOF y = x==y
But I really find this rather dubious. If you need different Eq behaviour, then you should probably implement everything completely by hand – different Eq instance means pretty much everything should treat F 0 and I 0 as if they were the same value, which is not what the compiler assumes with derived instances.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With