3

タイプクラスがHaskellの別のタイプクラスを暗示することは可能ですか? たとえば、「属性」で順序付けできる「もの」がたくさんあるとします。

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

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

繰り返しを避けるために、「キーで順序付け可能な」型クラスを定義できます

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

次に、のインスタンス宣言は次のPersonようになります

instance OrdByKey Person where
  orderKey Person p = age p

現在、これは明らかに複数の理由で機能しません。それがまったく可能かどうか疑問に思いますか?

4

2 に答える 2

2

指定した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フィールドとフィールドの両方で比較するバージョンを持つことはできませんssnnewtype各フィールドを でラップして、各フィールドが一意の型を持つようにすることで、これを回避できます 。あなたの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クラスが役立つインスタンスは考えられず<=、同じタイプの複数のバージョンでクラスをオーバーロードしようとすると、実際には混乱するようです。

于 2013-02-08T17:04:36.730 に答える
0

あなたはこれを行うことができます:

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

これで、標準の<=オペレーターがsに取り組みPerson、年齢を比較します。

于 2013-02-08T16:58:34.310 に答える