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