简体   繁体   中英

Haskell error: "No instance fo (Eq Time) arising from a use of '=='

I'm pretty new to Haskell and I don't get why I can't compare those 2 variables. I have this function:

nextMinute :: Date -> Time -> DateTime
nextMinute date time = if time == Time 23 59
                       then DateTime (tomorrow date) newDay
                       else DateTime date (timeSucc time)

I get this error message:

Time.hs:88:32:
    No instance for (Eq Time) arising from a use of ‘==’
    In the expression: time == Time 23 59
    In the expression:
      if time == Time 23 59 then
          DateTime date newDay
      else
          DateTime date (timeSucc time)
    In an equation for ‘nextMinute’:
        nextMinute date time
          = if time == Time 23 59 then
                DateTime date newDay
            else
                DateTime date (timeSucc time)

Instead of using == , you can just pattern-match:

nextMinute :: Date -> Time -> DateTime
nextMinute date (Time 23 59) = DateTime (tomorrow date) newDay
nextMinute date time         = DateTime date            (timeSucc time)

It's probably fine to add Eq to your Time type, but there's no particular need to, when there's an equally good solution with pattern matching.

Equality comparison is a bit of a code smell in Haskell. If you find yourself using it, always consider whether you shouldn't better use pattern matching, as demonstrated in amalloy's answer. Pattern matching tends to be more efficient, and often still works even when equality really isn't possible (eg on infinite-length lists ).

Apart from that, IMO there are types where you should better avoid any assumption of equality altogether. This includes all types representing some continuous physical quantity, like indeed time. Mathematically speaking, two such values are effectively never equal . From a more practical viewpoint, your approach has the risk that if you somehow overshoot the exact value 23:59 (eg add a minute and a second before checking the date transition), then the wrap will never happen at all!

Therefore, I recommend to only compare such values with less/greater operators.

nextMinute :: Date -> Time -> DateTime
nextMinute date time
  | h' >= 24   = DateTime (tomorrow date) newDay
  | otherwise  = DateTime date naïveSucc
 where naïveSucc@(Time h' _) = timeSucc time

In fact equality on infinite-length list has the much the same problem as equality on real numbers, if you picture those as infinite-length decimal expansions. Of course, the numeric types we normally use aren't in fact infinite-precision, but curiously it often helps to pretend so: really, floating point numbers are subject to all kinds of nasty uncertainty issues, but if you simply don't attempt to compare all digits but only a lazy selection, then you'll be mostly fine.


As dfeuer remarks, you can also compare Time s directly with < , if you make an Ord instance. Like Eq , this can be derived automatically

data Time = Time { ... }
 deriving (Show, Eq, Ord)

but watch out: the record fields need to be in the correct order for lexicographic order to make sense (you don't want 9:45 > 10:15 !)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM