Reputation: 4219
I have a typeclass Transforming
, with two complete definitions apply
and fill
. Here it is:
import Data.Set (Set, foldr, union, empty, map, singleton)
import Prelude hiding (foldr, map)
class Transforming t where
apply :: (Ord a) => t a -> a -> Set a
apply trans = fill trans . singleton
fill :: (Ord a) => t a -> Set a -> Set a
fill trans = foldr union empty . map (apply trans)
(The exact specifics are more or less unimportant to the question, but I thought I would include them)
Now the way I have it set up to infer member functions is nice, but often naive. So if I am making an instance of Transforming
I may want to implement either apply
or fill
depending on what is most efficient, and let Haskell handle the other. (Or in rare cases implement both) And this is fine that is kind of the point of having a typeclass. However at this point I would like to make a new datatype as follows
data SimpleTransform a = SingletonTrans (a -> a) | GenericTrans (a -> Set a)
Now when making this SimpleTransform
an instance of Transforming
I want to implement fill
for instances constructed with SingletonTrans
and implement apply
for instances constructed with GenericTrans
. I would like to write something like this:
instance Transforming Transform where
fill (SingletonTrans f) = map f
apply (GenericTrans f) = f
However this doesn't work. Since Transform
is a single datatype I need to completely define one of the two minimal definitions rather than partially define both. Now there are ways around this problem. The simplest is to just fully define both instances, however this is a super simple example and as the number of complete definitions in my typeclass grows and the number of constructors grows this begins to require repeating lots of code.
Is there a way I could use different complete definitions for different constructors of the same datatype? Is there some way I ought to restructure my code in order to make this unnecessary?
Upvotes: 3
Views: 161
Reputation: 89053
Another way, using Data.Functor.Sum
newtype SingletonTrans' a = SingletonTrans' (a -> a)
instance Transforming SingletonTrans' where
fill (SingletonTrans' f) = map f
newtype GenericTrans' a = GenericTrans' (a -> Set a)
instance Transforming GenericTrans' where
apply (GenericTrans' g) = g
instance (Transforming f, Transforming g) => Transforming (Sum f g) where
fill (InL f) = fill f
fill (InR g) = fill g
apply (InL f) = apply f
apply (InR g) = apply g
type SimpleTransform = Sum SingletonTrans' GenericTrans'
You can even use PatternSynonyms
to make SimpleTransform
act just like the original definition:
pattern SingletonTrans :: (a -> a) -> SimpleTransform a
pattern SingletonTrans f = InL (SingletonTrans' f)
pattern GenericTrans :: (a -> Set a) -> SimpleTransform a
pattern GenericTrans g = InR (GenericTrans' g)
Upvotes: 5
Reputation: 152707
Here is one way:
applyDef trans = fill trans . singleton
fillDef trans = foldr union empty . map (apply trans)
class Transforming t where
apply :: (Ord a) => t a -> a -> Set a
apply = applyDef
fill :: (Ord a) => t a -> Set a -> Set a
fill = fillDef
instance Transforming Transform where
fill (SingletonTrans f) = map f
fill trans = fillDef trans
apply (GenericTrans f) = f
apply trans = applyDef trans
Upvotes: 5