避免Functor Instance Boilerplate

时间:2014-01-04 00:59:00

标签: haskell functor boilerplate

我有一些数据类型Verb 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)

如果那不是样板,那么我不知道是什么。我可以想象,如果我必须前进到Applicative Functors等等,这将成为一种真正的痛苦。有没有更好的方法来编写它而不改变Verb p的结构?由于我编写Verb p的方式,似乎我注定要为每个数据类型构造函数声明一个实例。希望我被证明是错的。

1 个答案:

答案 0 :(得分:12)

查看DeriveFunctor扩展程序。如名称提示,它允许您只需将Functor添加到deriving列表中。不幸的是,这不会扩展到ApplicativeMonad,因为与Functor不同,这些通常没有一种方式来定义实例,但是多个非等价的可能性。

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

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