9

It's well-known that Monad instances ought to follow the Monad laws. It's perhaps less well-known that Functor instances ought to follow the Functor laws. Nevertheless, I would feel fairly confident writing a GHC rewrite rule that optimizes fmap id == id.

What other standard classes have implicit laws? Does (==) have to be a true equivalence relation? Does Ord have to form a partial order? A total order? Can we at least assume it's transitive? Anti-symmetric?

These last few don't appear to be specified in the Haskell 2010 report nor would I feel confident writing rewrite rules taking advantage of them. Are there any common libraries which do, however? How pathological an instance can one write confidently?

Finally, assuming that there is a boundary for how pathological such an instance can be is there a standard, comprehensive resource for the laws that each type class instance must uphold?


As an example, how much trouble would I be in to define

newtype Doh = Doh Bool
instance Eq Doh where a == (Doh b) = b

is it merely hard to understand or will the compiler optimize incorrectly anywhere?

J. Abrahamson
  • 72,246
  • 9
  • 135
  • 180
  • 4
    Note that quite a lot of sensible-sounding laws (including ones tacitly assumed by existing code) one might propose for `Ord`, `Num`, and related classes will be violated by the instances for `Float` and `Double`. For example, using NaN as a key breaks lookups in `Data.Map` and precision issues mean that floating point addition is not associative. – C. A. McCann Feb 13 '13 at 19:48
  • 3
    @C.A.McCann that just means the prelude has instances it shouldn't! `Float` and `Double` should not be members of `Eq` – Philip JF Feb 13 '13 at 20:07
  • 2
    @PhilipJF: On one hand, I agree. But their `Num` instance is little better, and following that line of argument to its logical conclusion leads to removing every instance that would make floating point types practically useful. Incidentally, [I gave a demonstration of how broken their `Ord` instance is in an old post.](http://stackoverflow.com/a/6399798/157360) – C. A. McCann Feb 13 '13 at 20:13
  • @C.A.McCann I'm okay with the instances, just so long as those types are limited to special "performance" oriented modules. Why have floating point types in the `Prelude` at all? If you actually need a float (rather than a ratio or a constructive real) you probably know what you are doing anyways. – Philip JF Feb 13 '13 at 20:16
  • 1
    It might be worth noting that in Standard ML, [reals are not equality types](http://www.smlnj.org/doc/Conversion/types.html#Real-equality). So at least one other language went for correctness over utility. :) – Daniel Lyons Feb 13 '13 at 21:51
  • This question was prompted through the realization that Reals aren't (decidable) equality types. Re http://math.andrej.com/2006/03/27/sometimes-all-functions-are-continuous/ and... a comment in another SO question I can't find right now – J. Abrahamson Feb 13 '13 at 22:45

3 Answers3

5

The Haskell report mentions laws for:

  • Functor (e.g. fmap id == id)
  • Monad (e.g. m >>= return == m)
  • Integral (e.g. (x ‘quot‘ y)*y + (x ‘rem‘ y) == x)
  • Num (abs x * signum x == x)
  • Show (showsPrec d x r ++ s == showsPrec d x (r ++ s))
  • Ix (e.g. inRange (l,u) i == elem i (range (l,u)))

That is all I could find. Specifically, the section about Eq (6.3.1) mentions no laws, neither does the next one about Ord.

David
  • 8,275
  • 5
  • 26
  • 36
4

My own view of what the laws "ought to be" is not upheld by all standard instances, but I think

  • Eq should be an equivalence relation.
  • Ord should be a total order
  • Num should be a ring, with fromInteger a ring homomorphism, and abs/signum behaving in the obvious ways.

Much code will assume these "laws" to hold even though they don't. This is not a Haskell specific problem, early C allowed compiler to reorder arithmetic according to algebraic laws, and most compilers have an option to do reenable such optimization even though they are not permitted by the current standard and may change your programs results.

Philip JF
  • 28,199
  • 5
  • 70
  • 77
  • I basically agree with you, though to my knowledge nothing goes badly if these denotations are violated. That's the heart of what I'd like to know—how wrong am I there? Is defining a non-reflexive `Eq` instance not just "challenging" but also actually optimized to be incorrect? – J. Abrahamson Feb 13 '13 at 22:47
  • 1
    @tel I doubt "optimized to be incorrect" but certainly "makes many functions have unpredictable results" – Philip JF Feb 13 '13 at 22:53
  • Yeah, in my mind there's "hard" failure where a rewrite rule messes you up and "soft" failure where you go against predominant assumptions throughout the language and it just gets really confusing. Both are bad, but one is worse. Probably the quintessential example would be `Eq Double`. – J. Abrahamson Feb 13 '13 at 23:39
  • @tel it doesn't just get really confusing. Certain functions will give incorrect results. Consider the behavior of a `Map Float a` (where Float can violate ordering assumptions), or `sortBy` and `nubBy` with types that violate ordering/equality assumptions. You will get incorrect behavior. I would consider this a hard failure, although it has nothing to do with the compiler's behavior. – John L Feb 14 '13 at 01:39
  • I suppose in my mind that just means that you're anticipating an instance different from the implementation. I anticipate that `nub` on a `[Double]` should remove duplicates, but since denotational `Double` equality is generally non-decidable it's really my internal denotation of how equality works that's out-of-whack. That's a far different concern from a compiler rewrite rule like `forall x. x == x = True` which is definitely correct if `Eq` is decidably reflexive, but could be invalidated. – J. Abrahamson Feb 14 '13 at 03:05
  • @tel: `Eq Float` makes more sense if you think of it as identity instead of numeric equality--think of how reference equality is used in some languages. NaN violates intuitive assumptions, but otherwise is well-behaved. On the other hand, `Ord Float` is absolutely broken by any reasonable standard, which can be demonstrated by using `compare` on NaNs. The link in my comment on the question demonstrates the consequences of this. – C. A. McCann Feb 14 '13 at 16:59
  • 1
    Identity depends on some global state---my Agda-addled mind says it means that `Eq` uses a global state level proof `refl :: Eq x x`. – J. Abrahamson Feb 14 '13 at 18:30
1

It used to be that breaking the Ix laws would let you do just about anything. These days I think they've fixed that. More info here: Does anyone know (or remember) how breaking class laws could cause problems in GHC?

Community
  • 1
  • 1
sclv
  • 38,665
  • 7
  • 99
  • 204