Sort a list of custom data types by an attribute in Haskell

advertisements

Let's say we have a custom data type:

data Person = Person {  first_name :: String,
        last_name :: String,
        age :: Int
        } deriving (Ord, Eq, Show)

Let's also say I have a list of these Person data types. I've already made a function that sorts these Persons in order, but this is limited to the first value of each person, first_name. What I'm trying to do is modify the Person data type so that this sort function sorts by the age instead of first_name (besides just swapping the value order so that age is first). I know I need to use the instance keyword to write my own compare function for Ord. This is where I'm stuck. Can anyone help me out?

Edit: Yep, this is HW-- I unfortunately need to do it the way I'm describing.


The easiest and most flexible way to deal with that problem, given that there are multiple valid ways to sort Person values, is not by changing the Ord instance, but by using a custom sorting function.

import Data.List (sortBy)
import Data.Ord (comparing)

sortByAge :: [Person] -> [Person]
sortByAge = sortBy (comparing age)

sortBy :: (a -> a -> Ordering) -> [a] -> [a] makes a custom sorting function from a comparison function, while comparing :: Ord a => (b -> a) -> b -> b -> Ordering makes such a comparison function given, for instance, a field acessor. Be sure to have a good look at the involved type signatures in order to grok how everything fits together.

If you do need to change the Ord instance, here is how it would go. The syntax is as follows:

instance Ord Person where
    compare = undefined -- placeholder

The documentation tells us that, from all of the Ord methods, we just need to implement compare. Now, what should we replace undefined with? We want the comparison to be based on age, which is an Int field. Since Int is an instance of Ord, the answer is immediate:

instance Ord Person where
    compare x y = compare (age x) (age y)

Incidentally, the definition of comparing is:

comparing :: (Ord a) => (b -> a) -> b -> b -> Ordering
comparing p x y = compare (p x) (p y)

And so we could write the instance in the style we used for the first solution:

instance Ord Person where
    compare = comparing age