I would like to know the reasons why enum typeclass does not inherit the ord typeclass. The succ function in enum essentially imposes an order on the values and yet ord is not a required constraint.
Edit: Also, for other functions in the typeclass, there seems to be an implicit assumption about ord operators being defined for them. As shown here https://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:enumFromTo
a possible implementation being enumFromTo n m | n <= m = n : enumFromTo (succ n) m | otherwise = []
The Ord
typeclass is meant to represent total orders, which among other things requires transitivity: if a < b
and b < c
, then a < c
.
A superclass should be necessary for the definition of an instance of a class.
An Enum
, though it might impose an order (though not a necessarily total one) on its elements, doesn't use or require a total ordering.
As an example, consider everyone's favorite decision-making algorithm:
data RPS = Rock | Paper | Scissors deriving (Eq, Ord)
instance Enum RPS where
succ Rock = Paper
succ Paper = Scissors
succ Scissors = Rock
The Ord
instance derived from the definition is total, and straightforward: Rock
is the smallest element, Scissors
the largest, and Paper
comes between.
The order implied by this enumeration isn't total. Given succ
, you might assume that Rock < Paper
and Paper < Scissors
, but then Rock < Scissors
should be true, which is not implied by succ Scissors = Rock
.
The Ord
instance wouldn't be useful in defining the Enum
shown above.
Unfortunately, a proper instance declaration should use toEnum
and fromEnum
, from which all the other methods (including succ
) can be derived. This makes the definition a little more verbose. Following the model of DaysOfWeek
instance Enum RPS where
-- *We* know that n `mod` 3 has 3 possible results, but
-- the exhaustivity checker does not, so use _ in place of 2
toEnum n = case n `mod` 3 of
0 -> Rock
1 -> Paper
_ -> Scissors
fromEnum Rock = 0
fromEnum Paper = 1
fromEnum Scissors = 2
-- The generated definitions don't handle wrapping. Without these,
-- [Paper..Rock] would produce [], not [Paper, Scissors, Rock].
enumFromTo wd1 wd2
| wd1 == wd2 = [wd1]
enumFromTo wd1 wd2 = wd1 : enumFromTo (succ wd1) wd2
enumFromThenTo wd1 wd2 wd3
| wd2 == wd3 = [wd1, wd2]
enumFromThenTo wd1 wd2 wd3 = wd1 : enumFromThenTo wd2 (toEnum $ (2 * fromEnum wd2) - (fromEnum wd1)) wd3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With