Search code examples
haskellalgebraic-data-types

Haskell Cyclic Ord Relationship


I was trying to define a data type for rock paper scissors and came up with something like this:

data Hand = P | S | R deriving (Show, Eq)

instance Ord Hand where
  compare R P = LT
  compare P R = GT
  compare R S = GT
  compare S R = LT
  compare P S = LT
  compare S P = GT
  compare _ _ = EQ

While writing all that I was wondering if there's any way to define the data type to just have it derive Ord and then specify that compare R P = LT and compare P R = GT instead of having to write all the comparisons by hand, for three elements it's okay but it would get tedious with each added element.


Solution

  • What you describe here is not an order relation. An order relation is:

    1. reflexive, all values x are less than or equal tot itself;
    2. antisymmetric: if x is less than or equal to y and y is less than or equal to x, then x is equal to y;
    3. transitive: if x is less than or equal to y, and y is less than or equal to z, then x is less than or equal to z.

    Your definition is not transitive. Indeed: S is less than R, R is less than P, but S is not less than P. I therefore would strongly advice you not to use Ord, since for instance sorting, etc. make use of these invariants.

    What you can do is let it derive from Ord automatically:

    data Hand = P | S | R deriving (Show, Eq, Ord)

    and then define a function beats:

    beats :: Hand -> Hand -> Ordering
    beats R P = LT
    beats P R = GT
    beats x y = compare x y