9

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

Petr
  • 62,528
  • 13
  • 153
  • 317

2 Answers2

9

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.

kosmikus
  • 19,549
  • 3
  • 51
  • 66
3

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.

Sjoerd Visscher
  • 11,840
  • 2
  • 47
  • 59
  • Does GHC derive instances of `Generic1` automatically? – Petr Jul 19 '13 at 20:24
  • 1
    @PetrPudlák Not fully automatically. But with the `DeriveGeneric` language extension, you can use `deriving Generic` as well as `deriving Generic1` (where the latter only works for datatypes with at least one parameter, the last parameter being of kind `*`). – kosmikus Jul 19 '13 at 20:27
  • @kosmikus Thank you. Unfortunately for my goal I'd like to work with more complex kinds, so probably I'll have to use Template Haskell. – Petr Jul 20 '13 at 07:38
  • @PetrPudlák Ah, but your question was just about the normal Functor class. If you want more complex kinds, then perhaps you can explain what you really want. – kosmikus Jul 20 '13 at 10:20
  • @kosmikus Yes, I wanted to start with a simpler example like `Functor` and I'm definitely glad for all the information. I'll process it and then perhaps ask another question. – Petr Jul 20 '13 at 15:21