4

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)
duplode
  • 33,731
  • 7
  • 79
  • 150
  • 1
    I'm surprised that this works, since a `data`type needs to start with an **u**percase. – Willem Van Onsem Jul 31 '18 at 14:24
  • Furthermore it is not really clear to me what the problem is. What do you mean with "two different time sets with the use of `/=`? You can define a `different :: Time -> Time -> Bool`, or make it an instance of `Eq`. – Willem Van Onsem Jul 31 '18 at 14:26
  • I do not want to use Eq. Yes, I could define "different", but my question is if it is possible to combine different and same in one function. – Bronia Berlin Jul 31 '18 at 14:31
  • "combine different and same in one function" I don't understand what you mean by this. I assume that the result of `different a b` is different than the result of `same a b`, right? If so, then they must be two different functions. – Code-Apprentice Jul 31 '18 at 14:32
  • @BroniaBerlin: yes, add an extra parameter that specifies if you want the "same" or "different" flavor. But the standard approach is to make `Time` an instance of `Eq`, such that one can *reuse* the `(==)` for all kinds of objects. Furthermore all of a sudden, you can use functions like `nub`, that have as condition that the elements they `num` are of an `Eq` type. – Willem Van Onsem Jul 31 '18 at 14:32
  • `h` looks like a canonicalization function; it essentially converts any `Time` to `Localtime` form, except it's a tuple. By the way, it is incomplete for reality; we do have odd timezones like +12:45. – Yann Vernier Jul 31 '18 at 14:58
  • I guess what you're looking for (for whatever reason) is something like 'named implementations' that Haskell doesn't have and Idris does http://docs.idris-lang.org/en/latest/tutorial/interfaces.html#named-implementations – Mika Feiler Jul 31 '18 at 22:14

1 Answers1

14

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.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 4
    Thank you! That is very helpful. Could you tell me how I would compile this in my ghci? – Bronia Berlin Jul 31 '18 at 14:39
  • 1
    @BroniaBerlin: you can write it between `:{` and `:}` lines to implement a multi-line item: https://stackoverflow.com/questions/8443035/multi-line-commands-in-ghci – Willem Van Onsem Jul 31 '18 at 14:40
  • I've never used Eq before so I am kinda lost right now, so if I would like to compare two time sets I would write what exactly? – Bronia Berlin Jul 31 '18 at 14:51
  • >Prelude : Time (Localtime 13 20) (Localtime 14 20) would be false with my previous function – Bronia Berlin Jul 31 '18 at 14:52
  • 1
    @BroniaBerlin: and it is `False` if you write `Localtime 13 20 == Localtime 14 20`. – Willem Van Onsem Jul 31 '18 at 14:54
  • "how I would compile this in my ghci?" GHCi is an interpreter and not capable of compilation. I know this is not actually answering your question, which is addressed by Willem, but it is worth realizing when the question itself is misleading/unanswerable and why. – Thomas M. DuBuisson Jul 31 '18 at 15:47
  • 1
    you make the best answer of this kind, I always enjoy reading your posts. Congrats. +1 – developer_hatch Jul 31 '18 at 15:50