Haskell: Typeclass implies a different model

Is it possible for another class to appear on the class line in Haskell? For example, let's say there are a bunch of "things" that can be ordered by "attribute":

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

Person p1 <= Person p1 = (age p1) <= (age p2)

      

To avoid repetition, it was possible to define a class of type "ordered by key"

class OrdByKey o where
  orderKey :: (Ord r) => o -> r
  x <= y = (orderKey x) <= (orderKey y)

      

Then the instance declaration for Person

might look like this:

instance OrdByKey Person where
  orderKey Person p = age p

      

Now this obviously doesn't work for several reasons. I wonder if this is possible at all?

+3


source to share


2 answers


As you pointed out, a class OrdByKey

can only have one instance per type, when it sounds like you would like to declare an instance for each field in your record type.

For this you will need to put the field type in the class as well. This allows you to do something like the following:

{-# LANGUAGE MultiParamTypeClasses #-}

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

class (Ord r) => OrdByKey o r where
   orderKey :: o -> r

instance OrdByKey Person Int where
  orderKey p = age p

x <=? y = (orderKey x :: Int) <= (orderKey y :: Int)

      

However, you can only have one instance per field type, so if your Type Person

looks like

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

      

you won't be able to compare both version name

and fields ssn

. You can work around this by wrapping each field in newtype

, so each field has a unique type. So your type Person

will look like



data Person = Person { name :: Name, age :: Age, ssn :: SSN}

      

This will lead to what will be around newtypes

.

The real downside to this is the need to specify the return type for orderKey

. I would recommend using the function on

from Data.Function

to write the appropriate comparison functions. I think how

compareByKey :: (Ord b) => (a -> b) -> a -> a -> Bool
compareByKey = on (<=)

      

summarizes your idea of ​​"can be compared using some kind of key". You just need to give it to a function that fetches that key, which will exactly belong to the function for your type Person

, in this case.

I can't think of an instance in which the class OrdByKey

would be useful, and trying to overload <=

multiple versions for the same type seems like it would be wrongly confused in practice.

+2


source


You can do it:

instance Ord Person where
    compare p1 p2 = compare (age p1) (age p2)

      



Now the standard <=

operator will work on Person

and compare their ages.

0


source







All Articles