推断Eq类型类

时间:2015-03-13 09:26:13

标签: haskell typeclass

我正在编写类似于应用程序的CRUD,并且通过主键进行大量查找(主键可以有不同的类型)。所以我定义了以下类型类:

{-# LANGUAGE MultiParamTypeClasses #-}

class Eq b => HasPK a b where
  getPK :: a -> b

现在我可以写:

import Data.Maybe

lookupPK :: HasPK a b => b -> [a] -> Maybe a
lookupPK s = listToMaybe . filter ((== s) . getPK)

现在,当我想用​​PK比较两件事时,我只想比较他们的PK。所以,我试图定义这个:

{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE UndecidableInstances #-}

instance (HasPK a b) => Eq a where
  (==) = (==) `on` getPK

但现在它给了我:

src/Utils.hs:61:10: Could not deduce (HasPK a b0) …
      arising from the ambiguity check for an instance declaration
    from the context (HasPK a b)
      bound by an instance declaration: HasPK a b => Eq a
      at /home/utdemir/workspace/.../Utils.hs:61:10-28
    The type variable ‘b0’ is ambiguous
    In the ambiguity check for: forall a b. HasPK a b => Eq a
    To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
    In the instance declaration for ‘Eq a’
Compilation failed.

任何人都可以向我解释这个错误吗?我是在正确的轨道上,还是有更安全的方式来实现我想要的目标?

1 个答案:

答案 0 :(得分:9)

你需要一个功能依赖:使用

class Eq b => HasPK a b | a -> b where
  getPK :: a -> b

并启用GHC指向的任何扩展名。

问题在于可能为同一类型设置多个PK, 如在

instance HasPK MyType Int where ...
instance HasPK MyType String where ...

由于可以在以后添加上述双实例,因此(==) `on` getPK之类的代码可能不明确。实际上,当添加上述实例时,它没有指定是使用Int PK还是String实例。


然而,像

这样的实例
instance (HasPK a b) => Eq a where
  ...

可能会导致问题,因为它与任何其他Eq实例重叠。这里要小心。我会改为写

equalPK :: HasPK a b => a -> a -> Bool
equalPK = (==) `on` getPK

然后为所有相关类型提供显式实例:

instance Eq MyType1 where (==) = equalPK
instance Eq MyType2 where (==) = equalPK
...

作为另一种选择,您可以使用类型系列,例如

class HasPK a where
   type PK a
   getPK :: a -> PK a

equalPK :: Eq (PK a) => a -> a -> Bool
equalPK = (==) `on` getPK

instance Eq MyType1 where (==) = equalPK
...