Question

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

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top