避免Functor实例的样板代码

3

我有一些数据类型动词p 的定义如下:

data Verb p =   Look          {getPreps :: p}
              | LookExtra     {getPreps :: p}
              | Touch         {getPreps :: p}
              | Smell         {getPreps :: p}
              | HearExtra     {getPreps :: p}
              | Hear          {getPreps :: p}
              | Taste         {getPreps :: p}
              | Pickup        {getPreps :: p}
              | PickupExtra   {getPreps :: p}
              | Move          {getPreps :: p}
              | MoveExtra     {getPreps :: p}
              deriving (Show,Ord,Eq)

我必须至少将这个数据类型变为Functor的一个实例。因此:

instance Functor Verb where
  fmap f (Look a)        = Look (f a)
  fmap f (LookExtra a)   = LookExtra (f a)
  fmap f (Touch a)       = Touch (f a)
  fmap f (Smell a)       = Smell (f a)
  fmap f (HearExtra a)   = HearExtra (f a)
  fmap f (Hear a)        = Hear (f a)
  fmap f (Taste a)       = Taste (f a)
  fmap f (Pickup a)      = Pickup (f a)
  fmap f (PickupExtra a) = PickupExtra (f a)
  fmap f (Move a)        = Move (f a)
  fmap f (MoveExtra a)   = MoveExtra (f a)

如果这不是样板代码,我就不知道什么是了。如果我必须进展到"应用函子"等更高级的内容,那么我可以想象这会变得非常痛苦。有没有更好的方法来编写这个,而不改变"动词p"的结构?由于我已经写了"动词p",似乎我注定要为每个数据类型构造器声明一个实例。希望我能被证明是错误的。
1个回答

12

请查看DeriveFunctor扩展。正如名称所示,它允许你将Functor简单地添加到deriving列表中。不幸的是,由于与Functor不同,ApplicativeMonad通常没有一种定义实例的方法,而是有多个不同的可能性。

在您的示例中,我会简化数据定义:

data VerbType = Look | LookExtra | ...
type Verb a = (VerbType, a)
-- or data Verb a = Verb { verbType :: VerbType, getPreps :: a }

2
DerivingFunctor非常有用,但对于这种情况,如果按照建议定义数据类型,则已经有一个函子实例(即((,) a))。 - user2407038
1
这是一个非常有用的扩展;我之前不知道这个。不过,我认为你的第二个建议(对我的数据类型进行一些轻微的重构)可能更普遍适用于我的情况。非常感谢。 - eazar001

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接