简体   繁体   English

如何在引入细微差异的同时“重用”来自另一个类型类的实例定义?

[英]How to "reuse" instance definitions from another typeclass while introduding minor differences?

I want to output my application's logs in JSON, but there are some ubiquitous data-types for which ToJSON instances are not defined - most notably SomeException and the entire Exception hierarchy of types.我想以 JSON 输出我的应用程序日志,但是有一些普遍存在的数据类型没有定义ToJSON实例 - 最显着的是SomeException和整个Exception类型的层次结构。

I have two choices:我有两个选择:

  1. Define instances of ToJSON for such data-types in my application在我的应用程序中为此类数据类型定义ToJSON实例
  2. Write my own type-class, say ToJsonLogs , and make it reuse ToJSON instances as much as possible.编写我自己的类型类,比如ToJsonLogs ,并使其尽可能重用ToJSON实例。

The first is the path of "least resistance" but it has other implications.第一个是“阻力最小”的路径,但它还有其他含义。 Since type-class instances are global in nature, I might end-up defining ToJSON instances that break something.由于类型类实例本质上是全局的,我最终可能会定义破坏某些内容的ToJSON实例。 Also, for the same data-structure, I might want the JSON in APIs to be different from the JSON in logs (for example, scrubbing keys, auth-tokens, and other sensitive data OR truncating very long text fields).此外,对于相同的数据结构,我可能希望 API 中的 JSON 与日志中的 JSON 不同(例如,清除密钥、身份验证令牌和其他敏感数据或截断很长的文本字段)。

This questions is about exploring the second option.这个问题是关于探索第二个选项。 How do I go about doing something like the following:我如何去做类似以下的事情:

class ToJsonLogs a where
  toJsonLogs :: a -> Aeson.Value

  default toJsonLogs :: (ToJSON a) => a -> Aeson.Value
  toJsonLogs = toJSON

instance ToJsonLogs SomeException where
  toJsonLogs = toJSON . displayException

I tried the above idea, but it failed at the very first step itself.我尝试了上述想法,但它在第一步本身就失败了。 Here's an example data-structure:这是一个示例数据结构:

data SyncResult = SyncResult
  { resAborted :: !Bool
  , resSuccessful :: !Int
  , resFailed :: ![(Int, SomeException)]
  } deriving (Show)

I can't derive ToJsonLogs without first deriving ToJSON for the entire data-structure. ToJsonLogs不首先为整个数据结构派生ToJSON ,我就无法派生ToJsonLogs Derivation of ToJSON fails because of SomeException .由于SomeExceptionToJSON失败。 Hence the title of this question.因此,这个问题的标题。

I even tried fooling around with Generics, but as usual, got stuck again .我什至尝试过玩泛型,但像往常一样, 再次卡住了

You are very close to a possible extension-free solution.您非常接近可能的无扩展解决方案。 The thing you should consider is to create a wrapper for the original ToJson class members:您应该考虑的是为原始ToJson类成员创建一个包装器:

class ToJsonLogs a where
  toJsonLogs :: a -> Aeson.Value

newtype WrapToJson a = WrapToJson a  -- actually an Identity

instance ToJson a => ToJsonLogs (WrapToJson a) where
  toJsonLogs (WrapToJson x) = toJson x

-- example
logInt :: Int -> Aeson.value
logInt x = toJsonLogs (WrapJson x)

If you want to restrict the wrapper only for ToJson instances, you will need to enable few extensions:如果您只想限制ToJson实例的包装器,则需要启用一些扩展:

{-# LANGUAGE GADTSyntax, ExistentialQuantifiaction #-}
data WrapToJson a where WrapToJson :: ToJson a => a -> WrapToJson a  

If you don't enjoy this wrapper, you may hide it under another definition of toJsonLogs :如果你不喜欢这个包装器,你可以将它隐藏在toJsonLogs另一个定义下:

toJsonLogs' :: ToJson a => a -> Aeson.value
toJsonLogs' = toJsonLogs . WrapToJson

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM