Haskell,当A类为B类提供足够的信息时,B的Gen实例

时间:2015-12-04 20:59:02

标签: haskell template-haskell

在为Collection / Container类型编写类时(如果我重新发明轮子,请指向现有类型),以提供一个通用界面来添加和删除任何' Collection'类型。

class (Eq (c a), Monoid (c a)) => Collection c a where
  emptyColl   :: c a -> Bool
  splitColl   :: c a -> (a, c a)
  toColl      :: a -> c a
  size        :: c a -> Int
  combineColl :: a -> c a -> c a
  (>|<)       :: a -> c a -> c a
  a >|< c     =  combineColl a c

我注意到Collection的实例也可以是Foldable的实例。使用splitColl和emptyColl。所以你也不需要写一个Foldable,如果你在几个类上做了这样的构造,那么可以节省大量的时间,写出琐碎的实例。

我试图让Collection成为Foldable的一个实例。然而,类似乎不能从其他类实例化它们吗? 我收到以下错误消息:

instance Functor (Collection c) where

The first argument of ‘Functor’ should have kind ‘* -> *’,
  but ‘Collection c’ has kind ‘* -> GHC.Prim.Constraint’
In the instance declaration for ‘Functor (Collection c)’ 

instance Functor (Collection c a) where

 The first argument of ‘Functor’ should have kind ‘* -> *’,
  but ‘Collection c a’ has kind ‘GHC.Prim.Constraint’
In the instance declaration for ‘Functor (Collection c a)’

我怎样才能获得所需的功能?我认为模板Haskell可能是去这里的方式,我从来没有用过它,一个例子会很棒:)

提前致谢!

PS:我一直在写haskell大约一年,想到的小小提示非常感谢

1 个答案:

答案 0 :(得分:2)

这方面的标准技巧是提供适当功能的实现,并让用户编写自己的实例。例如,您可以编写

fmapColl :: (Collection c a, Collection c b) => (a -> b) -> c a -> c b
fmapColl f ca
    | emptyColl ca = mkEmptyColl -- you don't have this in your class, but probably should
    | otherwise = case splitColl ca of
        (a, ca') -> f a >|< fmapColl f ca'

假设我们有一个合适的类型类,比如CFunctor

class CFunctor f where
    type ConstraintI f
    type ConstraintO f
    cfmap :: (ConstraintI i, ConstraintO o) => (i -> o) -> f i -> f o

然后对于Collection之类的给定Set个实例,我们可以使用最少的实际代码来实例化CFunctor

instance CFunctor Set where
    type ConstraintI Set = Ord
    type ConstraintO Set = Ord
    cfmap = fmapColl

您可以看到此模式 - 为用户定义一个“默认”实现,例如在base library's fmapDefault中放入他们的实例。