Not infrequently, one wants to implement the <=>
(comparison, or "spaceship") operator on a product data type, i.e., a class with multiple fields (all of which (we hope!) already have <=>
implemented), comparing the fields in a certain order.
def <=>(o)
f1 < o.f1 && (return -1)
f1 > o.f1 && (return 1)
f2 < o.f2 && (return -1)
f2 > o.f2 && (return 1)
return 0
end
This is both tedious and error-prone, especially with a lot of fields. It's error-prone enough that I frequently feel I should unit test that function, which just adds to the tediousness and verbosity.
Haskell offers a particularly nice way of doing this:
import Data.Monoid (mappend) import Data.Ord (comparing) -- From the standard library: -- data Ordering = LT | EQ | GT data D = D { f3 :: Int, f2 :: Double, f1 :: Char } deriving Show compareD :: D -> D -> Ordering compareD = foldl1 mappend [comparing f1, comparing f2, comparing f3]
(For those not familiar with fold
, the above expands to
comparing f1 `mappend` comparing f2 `mappend` comparing f3
which produces a function that can be applied to two D
s, to produce an Ordering
.)
The defintion of compareD
is so simple that it's obviously correct, and I wouldn't feel the need to unit test it even without static type checking.
Actually, the question may be even slightly more interesting than this, since I may not want to use just the standard <=>
operator, but sort in different ways at different times, e.g.:
sortByOrderings :: [a -> a -> Ordering] -> [a] -> [a] sortByOrderings = sortBy . foldl1 mappend sortByF3F1 = sortByOrderings [comparing f3, comparing f1] sortByF2F3 = sortByOrderings [comparing f2, comparing f3]
So, the questions:
- What's the typical way of implementing this sort of thing in Ruby?
- What's the nicest way of doing it using just what's defined in the standard libraries?
- How close can one get to the Haskell code above, and how reliable is it, in comparison? If necessary, how can one ensure that the fields have a properly implemented
<=>
or<
and>
operators?
Incidently, while this is a Ruby question, I'm happy to consider discussion of the Haskell techniques on-topic if the elders of this site so agree. Please feel free to comment on whether that's appropriate or not and, if it is, tag this post 'haskell' as well.
sortBy
thing that Haskell does, but it sure does a great job of dealing with default comparisons! – Butts