How to construct generic Functor instances using G

2019-04-21 20:44发布

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

2条回答
孤傲高冷的网名
2楼-- · 2019-04-21 21:25

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.

查看更多
在下西门庆
3楼-- · 2019-04-21 21:31

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.

查看更多
登录 后发表回答