避免 Haskell 中重复的实例声明

fat*_*ata 5 haskell dry instance deriving derivingvia

我的问题似乎与这个密切相关 。

我的代码解析 yaml 文件,重新排列对象并写入一个新的 yaml 文件。它工作得很好,但有一个特别难看的部分。

我必须将我的数据结构声明为 的实例,FromJson如下ToJson所示:

instance FromJSON Users where
  parseJSON = genericParseJSON (defaultOptions { fieldLabelModifier = body_noprefix })
instance ToJSON Users where
  toJSON = genericToJSON (defaultOptions { fieldLabelModifier = body_noprefix })
Run Code Online (Sandbox Code Playgroud)

问题是我必须对 8 个左右的其他情况重复此操作:

instance FromJSON Role where
  parseJSON = genericParseJSON (defaultOptions { fieldLabelModifier = body_noprefix })
instance ToJSON Role where
  toJSON = genericToJSON (defaultOptions { fieldLabelModifier = body_noprefix })

...
...
Run Code Online (Sandbox Code Playgroud)

我不知道如何避免这种重复。是否有某种方法可以仅声明两个函数一次(例如在新类中)并让所有这些数据类型从中派生?

解决方案(另请参阅 dfeuer 接受的答案):

我个人喜欢这个解决方案。你需要添加

{-# language DerivingVia #-}
{-# language UndecidableInstances #-}

newtype NP a = NP {unNP::a} 

instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (NP a) where
  parseJSON = fmap NP . genericParseJSON 
    (defaultOptions { fieldLabelModifier = body_noprefix })

instance (Generic a, GToJSON Zero (Rep a)) => ToJSON (NP a) where
  toJSON = genericToJSON (defaultOptions { fieldLabelModifier = body_noprefix }) . unNP
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样声明类型:

data User = User { ... } deriving (Show, Generic)
                         deriving FromJSON via (NP User)
                         deriving ToJSON via (NP User)
Run Code Online (Sandbox Code Playgroud)

dfe*_*uer 5

这就是这个相当新的DerivingVia扩展的用途。

{-# language DerivingVia #-}

newtype NP a = NP {unNP::a}

instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (NP a) where
  parseJSON = fmap NP . genericParseJSON 
    (defaultOptions { fieldLabelModifier = body_noprefix })

instance (Generic a, GToJSON Zero (Rep a)) => ToJSON (NP a) where
  toJSON = genericToJSON (defaultOptions { fieldLabelModifier = body_noprefix }) . unNP
Run Code Online (Sandbox Code Playgroud)

现在,你可以写

deriving via (NP User) instance FromJSON User
Run Code Online (Sandbox Code Playgroud)

或者

data User = ...
  deriving Generic
  deriving (FromJSON, ToJSON) via (NP User)
Run Code Online (Sandbox Code Playgroud)

等等。

这并没有比 leftaroundabout 的答案节省很多。然而,一旦添加了 的定义toEncoding,它就开始显得值得了。

注意:我没有测试过这些。