指定した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
クラスが役立つインスタンスは考えられず<=
、同じタイプの複数のバージョンでクラスをオーバーロードしようとすると、実際には混乱するようです。