问题
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