指定したOrdByKeyように、レコード タイプのフィールドごとにインスタンスを宣言できるようにしたい場合、クラスはタイプごとに 1 つのインスタンスしか持てません。
これを実現するには、フィールド タイプもクラス定義に入れる必要があります。これにより、次のようなことができます。
{-# 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)
ただし、フィールド タイプごとに 1 つのインスタンスしか持てないため、
Personタイプが次のように見える場合
data Person = Person { name :: String, age :: Int, ssn :: String}
nameフィールドとフィールドの両方で比較するバージョンを持つことはできませんssn。newtype各フィールドを でラップして、各フィールドが一意の型を持つようにすることで、これを回避できます
。あなたのPersonタイプは次のようになります
data Person = Person { name :: Name, age :: Age, ssn :: SSN}
newtypesしかし、それは多くの浮遊につながります。
これの本当の欠点は、関数の戻り値の型を指定する必要があること
orderKeyです。on関数 from
を使用Data.Functionして、適切な比較関数を作成することをお勧めします。のような機能だと思います
compareByKey :: (Ord b) => (a -> b) -> a -> a -> Bool
compareByKey = on (<=)
「いくつかのキーで比較できる」という考えを一般化します。Personこの場合、そのキーを抽出する関数を指定するだけで済みます。これは、タイプのアクセサー関数とまったく同じです。
OrdByKeyクラスが役立つインスタンスは考えられず<=、同じタイプの複数のバージョンでクラスをオーバーロードしようとすると、実際には混乱するようです。