Haskell:Typeclass暗示其他类型
是否有可能有一个类型类型暗示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
由于多种原因,现在这显然不起作用。 我想知道这是否可能?
正如你指定的那样, OrdByKey
类每个类型只能有一个实例,当你听起来像你希望能够为你的记录类型中的每个字段声明一个实例时。
要做到这一点,你也必须将字段类型放入类定义中。 这可以让你做下面的事情:
{-# 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)
但是,每个字段类型只能有一个实例,所以如果您的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
函数的返回类型。 我建议使用Data.Function
的on
函数来编写适当的比较函数。 我觉得像一个功能
compareByKey :: (Ord b) => (a -> b) -> a -> a -> Bool
compareByKey = on (<=)
推广你的“可以通过一些关键比较”的想法。 在这种情况下,您只需提供一个提取该键的函数,该函数就是Person
类型的访问函数。
我想不出一个OrdByKey
类会很有用的实例,并且试图用同一类型的多个版本来重载<=
好像它在实践中会混淆一样。
你可以这样做:
instance Ord Person where
compare p1 p2 = compare (age p1) (age p2)
现在标准的<=
运算符将在Person
工作并比较他们的年龄。