Как «повторно» использовать определения экземпляров из другого класса типов при внесении небольших различий? - PullRequest
2 голосов
/ 15 февраля 2020

Я хочу вывести журналы моего приложения в JSON, но есть некоторые повсеместно распространенные типы данных, для которых ToJSON экземпляры не определены, в частности SomeException и вся иерархия типов Exception.

У меня есть два варианта:

  1. Определить экземпляры ToJSON для таких типов данных в моем приложении
  2. Написать свой собственный класс типов, скажем ToJsonLogs, и заставьте его повторно использовать ToJSON экземпляров как можно больше.

Первый - это путь "наименьшего сопротивления", но он имеет другие последствия. Поскольку экземпляры класса типов имеют глобальный характер, я мог бы в конечном итоге определить ToJSON экземпляры, которые что-то нарушают. Кроме того, для той же структуры данных я мог бы пожелать, чтобы API JSON in отличался от JSON в журналах (например, очистка ключей, токенов и других конфиденциальных данных ИЛИ усечение очень длинных текстовых полей) .

Этот вопрос касается изучения второго варианта. Как мне go сделать что-то вроде следующего:

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

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

instance ToJsonLogs SomeException where
  toJsonLogs = toJSON . displayException

Я попробовал приведенную выше идею, но это не удалось на самом первом шаге. Вот пример структуры данных:

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

Я не могу получить ToJsonLogs без предварительного вывода ToJSON для всей структуры данных. Деривация ToJSON не удалась из-за SomeException. Отсюда и название этого вопроса.

Я даже пытался дурачиться с Generics, но, как обычно, снова застрял .

1 Ответ

0 голосов
/ 16 февраля 2020

Вы очень близки к возможному решению без расширения. Вам следует подумать о создании оболочки для исходных 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)

Если вы хотите ограничить оболочку только для ToJson экземпляров, вам нужно включить несколько расширений:

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

Если вам не нравится эта обертка, вы можете скрыть ее под другим определением toJsonLogs:

toJsonLogs' :: ToJson a => a -> Aeson.value
toJsonLogs' = toJsonLogs . WrapToJson
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...