Let's say that I have the following code:
import Data.List.Ordered
data Person = Person String String
deriving (Show, Eq)
main :: IO ()
main = print . show . sort $ [(Person "Isaac" "Newton"), (Person "Johannes" "Kepler")]
And in the same module, I want to be able to sort the list by both first name and by last name. Obviously I can't do this:
instance Ord Person where
compare (Person _ xLast) (Person _ yLast) = compare xLast yLast
instance Ord Person where
compare (Person xFirst _) (Person yFirst _) = compare xFirst yFirst
So, what are my options?
This page mentions "You can achieve this by wrapping the type in a newtype and lift all required instances to that new type." Can someone give an example of that?
Is there a better way?
The newtype method would be:
newtype ByFirstname = ByFirstname { unByFirstname :: Person }
instance Ord ByFirstname where
-- pattern matching on (ByFirstname (Person xFirst _)) etc
compare [...] = [...]
newtype ByLastname = ByLastname { unByLastname :: Person }
instance Ord ByLastname where
-- as above
Then the sorting function would be something like:
sortFirstname = map unByFirstname . sort . map ByFirstname
and similarly for ByLastname
.
A better way is to use sortBy
, compare
and on
, with functions for retrieving the first and last names. i.e.
sortFirstname = sortBy (compare `on` firstName)
(On that note, it might be worth using a record type for Person
, i.e. data Person = Person { firstName :: String, lastName :: String }
, and one even gets the accessor functions for free.)
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