Haskell, Gen instance of B when class A provides enough info for class B

天大地大妈咪最大 提交于 2019-12-20 03:20:38

问题


While writing a class for a Collection/Container type (btw point me towards existing types if i'm reinventing the wheel) to provide a general interface for adding and removing elements from any 'Collection' type.

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

I noticed instances of Collection could also be instances of Foldable. Using splitColl and emptyColl. So you don't need to write a Foldable as well, if you make constructs like this over several classes a significant amount of time, writing trivial instances, can be saved.

I tried to make Collection an instance of Foldable. However classes can't seem to be instantiated from other classes do they? I received the following error messages:

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)’

How could I get the desired functionality? I think template Haskell might be the way to go here, I never used it tho, an example would be great :)

Thanks in advance!

PS: I've been writing haskell for about a year, minor tips that come to mind are much appreciated


回答1:


The standard trick for this is to give implementations of the appropriate functions and let users write their own instances. For example, you might write

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'

Assuming we had a suitable type class, say, CFunctor:

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

Then for a given Collection instance like Set we could instantiate CFunctor with minimal actual code like this:

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

You can see this pattern -- defining a "default" implementation for users to put in their instances -- in the base library's fmapDefault, for example.



来源:https://stackoverflow.com/questions/34097332/haskell-gen-instance-of-b-when-class-a-provides-enough-info-for-class-b

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!