Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: How to put multiple instances in the same module?

Tags:

haskell

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?

like image 592
brooks94 Avatar asked Nov 28 '22 03:11

brooks94


1 Answers

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.)

like image 199
huon Avatar answered Nov 30 '22 23:11

huon