ekmett / intervals

Interval Arithmetic
http://hackage.haskell.org/package/intervals
BSD 2-Clause "Simplified" License
27 stars 13 forks source link

Extensional vs intensional Eq/Ord instances #26

Closed dmcclean closed 7 years ago

dmcclean commented 10 years ago
-- | For all @x@ in @X@, @y@ in @Y@. @x '==' y@
--
-- Only singleton intervals or empty intervals can return true
--
-- >>> (singleton 5 :: Interval Double) ==! (singleton 5 :: Interval Double)
-- True
--
-- >>> (5 ... 10 :: Interval Double) ==! (5 ... 10 :: Interval Double)
-- False
(==!) :: Eq a => Interval a -> Interval a -> Bool
I ax bx ==! I ay by = bx == ay && ax == by
{-# INLINE (==!) #-}

Should be I ax bx ==! I ay by = ax == bx && ay == by, right?

In my branch I am changing it and adding a test case that should be True but that isn't a singleton.

dmcclean commented 10 years ago

Wait a minute. Make that = ax == ay && bx == by, I got confused by the names.

dmcclean commented 10 years ago

Wait another much longer minute. Now I see that this is doing what it was intended to do.

My issue is that I wrote a test case that expected the Eq instance for Interval to be "the intervals are the same" and not "the values represented by the intervals are guaranteed to be the same because both intervals are equal singletons". Which one is better and what should I name the other one?

(Relatedly, the Ord instance does the same thing, whereas by my intuition I would have expected it not to exist.)

dmcclean commented 10 years ago

Possibly a good idea:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Intensional a = Intensional (Interval a)
  deriving (Show, Data, Typeable, Num, Fractional, ...)

instance Eq a => Eq (Intensional a) where
  (I ax bx) == (I ay by) = ax == ay && bx == by

-- intentionally (with a t) no Ord instance
-- or, there is an Ord instance but it is lexicographic, which could be useful if you want to make a Map or Set?
ekmett commented 10 years ago

The semantics of Eq are set up to be consistent with the semantics of Ord for the interval, so if they change then Ord would have to change as well.

I used to have code that actually cared about the current Ord instance, but I've since converted everything to use (<=?) or (<=!) explicitly rather than rely on the dangerous Ord instance we have. Using (==!) isn't correct anyways. e.g. Say you had a non-point interval that merely looks like a point due to rounding, etc.

We could in theory switch to a lexicographical ordering or an inclusion ordering that is ascending on oneside/descending on the other without too much pain.

dmcclean commented 10 years ago

I'm quite happy for my purposes with the Intensional newtype with the lexicographic ordering. The semantics of the current Eq and Ord instances also match the semantics of all the arithmetic, so that makes sense.

ekmett commented 10 years ago

One benefit of flipping is we could use Certainly and Possibly newtypes to get the (!) and (?) variants, but I'll just keep it in mind and not pull the trigger.

dmcclean commented 10 years ago

We could do that anyway, couldn't we? (I mean, without changing the actual Eq (Interval a) instance.)

ekmett commented 10 years ago

We could. I'd be interested in hearing from @bergey and @byorgey if they use the current Eq/Ord for Intervals at all.

byorgey commented 10 years ago

I am 95% sure we do not use the Eq or Ord instances.

phadej commented 8 years ago

FWIW, I use intervals with discrete carriers (Integer), and would like Eq instance to be structural, and don't care about Ord instance at all: I cannot come with practical use for Map (Interval a) v, as you'll probably want some semantics exploiting the fact that the key is an interval.

I like Certainly and Possibly idea.

ekmett commented 8 years ago

All right.

I'm okay with switching the semantics to be structural in both cases.

It'd be a major version bump, but I'll accept a patch that does this.