Say I have a data type like the following:
data Foo = Foo { field1, field2, field3 :: Int }
And I'd like to make it an instance of Ord
by comparing field1
, field2
, and field3
in a particular order.
I find it very annoying to write:
-- (we need Eq Foo to define Ord Foo)
instance Eq Foo where
x == y = all id [ f x == f y
| f <- [field1, field2, field3] ]
instance Ord Foo where
compare x y = case (comparing field1) x y of
EQ -> case (comparing field2) x y of
EQ -> (comparing field3) x y
ord -> ord
ord -> ord
Monads like Maybe
and Either
have some really nice support for this kind of thing, and I find myself wishing that Ordering
had something similar, e.g.
instance Ord Foo where
compare == comparing field1 >>= comparing field2 >>= comparing field3
...or something like that.
I've needed to do this for complex data types, where re-ordering fields in the definition and depending on default definitions for deriving (Eq, Ord)
were not possible, so I'm not interested in solutions that game the default instance declarations.
Is there a more elegant, or at least more terse, way to define this kind of ordering?
Thanks!
You can use the Monoid
instance for Ordering
and functions to good effect here:
instance Ord Foo where
compare = comparing field1 <> comparing field2 <> comparing field3
Another trick you can use that generalizes more readily to the Eq
instance is to use the instances for tuples:
equating = on (==)
reorder v = (field1 v, field2 v, field3 v)
instance Eq Foo where (==) = equating reorder
instance Ord Foo where compare = comparing reorder
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