Back

A nice way of sorting

This blog post is to explore a really clean way of sorting a list in Haskell.

Suppose you have a type such as

data Person = Person
  { name :: String
  , age  :: Int
  }

And you want to sort first by age, from older to younger, and then by name. A naive approach would be something like

comparePerson :: Person -> Person -> Ordering
comparePerson (Person name1 age1) (Person name2 age2)
  | age1 > age2   = LT
  | age1 < age2   = GT
  | name1 > name2 = GT
  | name1 < name2 = LT
  | otherwise     = EQ

And it pretty much works

lst = [ Person "A" 10
      , Person "B" 20
      , Person "B" 5
      , Person "C" 10
      ]

sorted = sortBy comparePerson lst
-- Which is [ Person "B" 20
--          , Person "A" 10
--          , Person "C" 10
--          , Person "B" 5
--          ]

But the function is ugly, and quite error prone to write. Enters the semigroup instance of Ordering, which is defined as

instance Semigroup Ordering where
  LT <> _ = LT
  EQ <> y = y
  GT <> _ = GT

We can then use comparing from Data.Ord with a couple of aliases and sort our list with code that is simpler and clearer.

asc  f = comparing f
desc f = flip (comparing f)

sorted = sortBy (desc age <> asc name) lst

Isn't that neat?