Haskell: Do standard libraries suggest that Eq and Ord are compatible?

Is this the next question for Inconsistent EQ and warrant instances? .

The question is: when declaring Eq and Ord instances for a type, you need to make sure that compare xy returns Eq if and only if x == y returns True ? Is it dangerous to create instances that violate this assumption? This is similar to a natural law, which could be supposed, but it is not explicitly indicated in the prelude, in contrast to, for example, the laws of the monad or functor.

The main answer: this is a little dangerous, since libraries may assume that this law holds.

Now my question is: execute any standard library (in particular, Set or Map ))? Is it dangerous to have a type with incompatible Eq and Ord if I rely only on the standard libraries that come with GHC? (If the questions with the large list are still acceptable, I would ask: what commonly used libraries pass this law?)

Change My use case is similar to the original question. I have a type with a custom Eq instance that I use very little. The only reason I want Ord is that I can use it as a Map domain; I don't care about a particular order and will never use it explicitly in code. Therefore, if I can use a derivative instance of Ord , then my life will be simpler and my code will become clearer.

+6
source share
3 answers

Defining Ord itself in the standard prelude requires an instance of Eq :

 class (Eq a) => Ord a where ... 

So it would be wrong to break

  x == y = compare xy == EQ x /= y = compare xy /= EQ 

As it would be violated (from the default definitions for these operators in Ord).

  x <= y = compare xy /= GT x < y = compare xy == LT x >= y = compare xy /= LT x > y = compare xy == GT 

Edit: use in libraries

I would be surprised if the standard libraries did not use the Ord == and /= operators. Specific operators ( == , /= , <= , < , >= , > ) are often more convenient than compare , so I expect them to be used in the code for map or filter s.

You can see that == used by key guards in Data.Map in fromAscListWithKey . This specific function only calls the Eq , but if the key is also an Ord instance, Ord compare will be used for other functions of the resulting map , which is the assumption that Eq == matches Ord compare and is tested for Eq .

As a library programmer, I would not be surprised if any of the special-purpose operators outperformed compare for a specific purpose. After all, why are they part of the Eq and Ord classes instead of being defined as polymorphic for all Eq or Ord instances. I could use them even when compare more convenient. If I did this, I would probably define something like:

 compareOp :: (Ord a) => Ordering -> Bool -> a -> a -> Bool compareOp EQ True = (==) compareOp EQ False = (/=) compareOp LT True = (<) compareOp LT False = (>=) compareOp GT True = (>) compareOp GT False = (<=) 
+6
source

To extend Cirdec's answer, typeclass instances should only be executed if a particular operation is somehow canonical. If there is a reasonable Eq that does not apply to a reasonable Ord , then it is best to choose a different Eq or not define Ord . It is easy enough to create a non-polymorphic function for the "other" equality.

A great example of this tension is a potential Monoid instance Monoid

 instance Monoid Int where mzero = 0 mappend = (+) 

which competes with another "obvious" instance of Monoid

 instance Monoid Int where mzero = 1 mappend = (*) 

In this case, the chosen path was to create an instance either because it is not clear that it is "canonical" over another. This is usually best suited for user expectation and prevents errors.

+3
source

I read this and your original question, so I will consider your general problem ....

Do you want this -

 Map BigThing OtherType 

and this -

 (==)::BigThing->BigThing->Bool 

One of these cases must be accurate, the other case must ignore some of its data for performance reasons. (this was (==), which should be accurate in the first question, but it looks like you can refer to the reverse question in this question ... The same answer anyway).

For example, you want the map to save the result only based on some label, for example

 `name::BigThing->String` 

but (==) should do a deep comparison. One way to do this is to define incompatible functions compare and (==) . But....

in this case it is not necessary. Why not just use a card

 Map String OtherThing 

and search as follows:

 lookup (name obj) theMap 

It’s rare to index data from a very large document ...

+1
source

All Articles