It is often required to implement the operator <=> (comparison or "spaceship") in the product data type, i.e. a class with several fields (all of which (hopefully!) already have <=> implemented), comparing the fields in a specific 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 tedious and error prone, especially with lots of fields. This is error prone enough, and I often feel like I should unit test this function, which simply adds to the tediousness and verbosity.
Haskell offers a particularly good way to do 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 extends to
comparing f1 `mappend` comparing f2 `mappend` comparing f3
which creates a function that can be applied to two D s, to create Ordering .)
The definition of compareD so simple that it is obviously correct, and I would not feel the need for a unit test even without checking the static type.
Actually, the question may even be a little more interesting than this, since I do not want to use only the standard operator <=> , but sort it differently at different times, for example:
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 are:
- What is a typical way to implement this kind of thing in Ruby?
- What is the best way to do this using what is defined in standard libraries?
- How close can you get the Haskell code above and how reliable is it in comparison? If necessary, how can one ensure that the fields have correctly implemented
<=> or < and > operators?
Incidentally, although this is a Ruby question, I will gladly consider discussing Haskell methods on the topic if the elders of this site agree. Please feel free to comment on whether this is suitable or not, and if so, check this 'haskell' post.