Search code examples
haskellalgebraic-data-types

Can I use == and /= without using Eq?


So I wrote this program and everything works fine ;) I would like to improve it. I would also be able to compile two different time sets with the use of /= and get True as an result. I am a little bit confused.

data Time = Localtime {hour, minute :: Int}
              | Globaltime {hour, minute, difference :: Int}
              | LocaltimeAM {hour, minute :: Int, am :: Bool} 
             deriving Show

same :: Time -> Time -> Bool
same u1 u2 = (h u1) == (h u2)
   where
      h :: Time -> (Int, Int) -- (Int, Int) = (hour, minute)
      h (Localtime h m) = (h, m)
      h (Globaltime h m d) = ((h+d+24) `mod` 24, m) 
      h (LocaltimeAM h m am) = (h + (if am then 0 else 12), m)

Solution

  • To answer the question in the title:

    Can I use == and /= without using Eq?

    You could - technically speaking - explicitly hide the Eq typeclass in the prelude, and then define a (==) and (/=) function yourself, but that would be a bad idea, since it would mean that you can no longer compare two integers with (==).

    What you probably want is to make Time an instance of the Eq typeclass, such that you can from now on write time1 == time2. We can make it an instance like:

    h :: Time -> (Int, Int) -- (Int, Int) = (hour, minute)
    h (Localtime h m) = (h, m)
    h (Globaltime h m d) = ((h+d+24) `mod` 24, m) 
    h (LocaltimeAM h m am) = (h + (if am then 0 else 12), m)
    
    instance Eq Time where
        t1 == t2 = (h t1) == (h t2)

    Haskell will automatically write the (/=) function for us (as the opposite of (==)), or you can decide to write the (/=) version, and then Haskell will write the (==) version. Of course you can also implement both.

    Making types a member of a typeclass actually can be useful. Take for example the nub :: Eq a => [a] -> [a] function. It requires that the type a is a member of the Eq typeclass, and performs some sort of "uniqness" filter: you provide it a list of elements, and it returns a list of non-equal elements. Now without any work to define a nub function for your Time type, by making Time an instance of the Eq type class, you can use nub on a list of Times.

    Of course you can not simply make a type an instance of all possible type classes. You should only make types an instance of the Eq typeclass if you can check if two items are the same (and there should thus be a "reasonable" idea when two items are equal). Furthermore most of these typeclasses come with "contracts": for example an equality relation (like defined by (==)) should be reflexive, symmetric, and transitive.