[英]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:我有两个选择:
ToJSON
for such data-types in my applicationToJSON
实例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
.由于
SomeException
, ToJSON
失败。 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.