如何使用GHC.Generics(或其他类似框架)构造通用Functor实例?

时间:2013-07-19 16:17:35

标签: generics haskell ghc scrap-your-boilerplate ghc-generics

我正在努力学习GHC Generics。在查看了几个示例之后,我想尝试创建一个通用的Functor实例(忽略GHC可以为我自动派生它们)。但是,我意识到我不知道如何使用Generics处理参数化数据类型,我见过的所有示例都是*类型。这是可能的,如果是的话,怎么样? (我也对其他类似的框架感兴趣,比如SYB。)

2 个答案:

答案 0 :(得分:8)

使用GHC Generics查找大量示例函数的最佳位置是generic-deriving package。那里有Functor类的通用定义。从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

要在数据类型上使用此功能,您必须派生Generic1而不是GenericGeneric1表示的关键区别在于它使用了编码参数位置的Par1数据类型。

答案 1 :(得分:3)

类型为* -> *的数据类型有一个Generic1类。使用它与类型*的数据类型大致相同,除了参数还有Par1。我在我的unfoldable package中使用过它。