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?
source to share
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.
source to share