I'm trying to learn GHC Generics. After reviewing several examples, I wanted to try to create a generic Functor
instances (disregarding that GHC can derive them automatically for me). However, I realized I have no idea how to work with a parametrized data types with Generics, all the examples I've seen were of kind *
. Is this possible, and if yes, how? (I'm also interested in other similar frameworks, such as SYB.)
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
回答1:
The best place to look for lots of example functions using GHC Generics is the generic-deriving
package. There's a generic definition of the Functor
class in there. Copying (slightly simplified) from Generics.Deriving.Functor
:
class GFunctor' f where
gmap' :: (a -> b) -> f a -> f b
instance GFunctor' U1 where
gmap' _ U1 = U1
instance GFunctor' Par1 where
gmap' f (Par1 a) = Par1 (f a)
instance GFunctor' (K1 i c) where
gmap' _ (K1 a) = K1 a
instance (GFunctor f) => GFunctor' (Rec1 f) where
gmap' f (Rec1 a) = Rec1 (gmap f a)
instance (GFunctor' f) => GFunctor' (M1 i c f) where
gmap' f (M1 a) = M1 (gmap' f a)
instance (GFunctor' f, GFunctor' g) => GFunctor' (f :+: g) where
gmap' f (L1 a) = L1 (gmap' f a)
gmap' f (R1 a) = R1 (gmap' f a)
instance (GFunctor' f, GFunctor' g) => GFunctor' (f :*: g) where
gmap' f (a :*: b) = gmap' f a :*: gmap' f b
instance (GFunctor f, GFunctor' g) => GFunctor' (f :.: g) where
gmap' f (Comp1 x) = Comp1 (gmap (gmap' f) x)
class GFunctor f where
gmap :: (a -> b) -> f a -> f b
default gmap :: (Generic1 f, GFunctor' (Rep1 f))
=> (a -> b) -> f a -> f b
gmap = gmapdefault
gmapdefault :: (Generic1 f, GFunctor' (Rep1 f))
=> (a -> b) -> f a -> f b
gmapdefault f = to1 . gmap' f . from1
To use this on a datatype, you have to derive Generic1
rather than Generic
. The key difference of the Generic1
representation is that it makes use of the Par1
datatype that encodes parameter positions.
回答2:
There is a Generic1
class for data types of kind * -> *
. Working with it is mostly the same as with data types of kind *
, except there's also Par1
for the parameter. I've used it in my unfoldable package for example.