首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >避免Haskell中的重复实例声明

避免Haskell中的重复实例声明
EN

Stack Overflow用户
提问于 2019-03-20 19:18:32
回答 3查看 276关注 0票数 5

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

我的代码解析一个yaml文件,备份对象并编写一个新的yaml文件。它运行得很好,但其中有一个特别丑陋的部分。

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

代码语言:javascript
复制
instance FromJSON Users where
  parseJSON = genericParseJSON (defaultOptions { fieldLabelModifier = body_noprefix })
instance ToJSON Users where
  toJSON = genericToJSON (defaultOptions { fieldLabelModifier = body_noprefix })

问题是,我必须在大约8宗其他个案中重复这点:

代码语言:javascript
复制
instance FromJSON Role where
  parseJSON = genericParseJSON (defaultOptions { fieldLabelModifier = body_noprefix })
instance ToJSON Role where
  toJSON = genericToJSON (defaultOptions { fieldLabelModifier = body_noprefix })

...
...

我想不出如何避免这种重复。有什么方法只声明这两个函数一次(例如在一个新的类中)并让所有这些数据类型从它派生出来吗?

解决方案(另见dfeuer接受的答案):

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

代码语言:javascript
复制
{-# 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

然后,您可以这样声明类型:

代码语言:javascript
复制
data User = User { ... } deriving (Show, Generic)
                         deriving FromJSON via (NP User)
                         deriving ToJSON via (NP User)
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-03-21 02:14:52

这就是非常新的DerivingVia扩展的目的,除其他外。

代码语言:javascript
复制
{-# 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

现在,你可以写

代码语言:javascript
复制
deriving via (NP User) instance FromJSON User

代码语言:javascript
复制
data User = ...
  deriving Generic
  deriving (FromJSON, ToJSON) via (NP User)

诸若此类。

这并没有为左撇子的回答省多少钱。但是,一旦添加了toEncoding的定义,它就开始看起来很有价值了。

警告:我没有测试过这一切。

票数 5
EN

Stack Overflow用户

发布于 2019-03-20 19:38:22

喜欢,

代码语言:javascript
复制
noPrefixParseJSON :: (Generic a, GFromJSON Zero (Rep a)) => Value -> Parser a
noPrefixParseJSON
    = genericParseJSON (defaultOptions { fieldLabelModifier = body_noprefix })
noPrefixToJSON :: (Generic a, GToJSON Zero (Rep a)) => a -> Value
noPrefixToJSON
    = genericToJSON (defaultOptions { fieldLabelModifier = body_noprefix })

instance FromJSON User where parseJSON = noPrefixParseJSON
instance ToJSON User where toJSON = noPrefixToJSON
instance FromJSON Role where parseJSON = noPrefixParseJSON
instance ToJSON Role where toJSON = noPrefixToJSON
...

当然,这仍然是重复的,但我想说,这不再是任何担心的原因。

票数 2
EN

Stack Overflow用户

发布于 2019-03-20 22:13:03

如果显式声明所有类似的实例证明过于繁琐,也许可以使用类似的幻影型来参数化数据类型

代码语言:javascript
复制
data User x = User { aa :: Int, bb :: Bool } deriving Generic

data Role x = Role { xx :: Int, dd :: Bool } deriving Generic

然后定义一个“标记”数据类型,如

代码语言:javascript
复制
data Marker

此数据类型将仅用作挂起实例的挂钩,如下所示

代码语言:javascript
复制
{-# language UndecidableInstances #-}
instance (Generic (f Marker), GFromJSON Zero (Rep (f Marker))) => FromJSON (f Marker) where 
    parseJSON = noPrefixParseJSON

值得吗?可能不会,因为数据类型的定义变得更加复杂。另一方面,您可以通过更改标记来更改序列化的各个方面,从而获得一些灵活性。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55268631

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档