Делаем обертку поверх универсального rep

У меня есть Rep A для какого-то типа, который я модифицирую с помощью некоторой функции forall x. Rep A x -> F (Rep A) x, где F - это семейство типов. Скажем, это обертывает все поля в Maybe. Я хочу сериализовать этот F (Rep A) в JSON. aeson предоставляет функции, которые сериализуют универсальные типы, но не предоставляет функций, которые работают с фактическим универсальным представлением.

Чтобы решить эту проблему, я подумал, что могу написать что-то вроде этого

newtype RepWrapper a = RepWrapper (F (Rep a))

с общим экземпляром

instance Generic a => Generic (RepWrapper a) where
    type Rep (RepWrapper a) = F (Rep a)

Тогда я могу просто использовать функции aeson для этого обернутого значения.

Проблема с вышеизложенным - это дополнительный параметр типа, который необходимо передать Rep. Насколько я могу судить, есть три способа сделать это.

newtype RepWrapper a = RepWrapper (forall x. F (Rep a) x)
data RepWrapper a = forall x. RepWrapper (F (Rep a) x)
newtype RepWrapper a x = RepWrapper (F (Rep a) x)

Первый способ позволяет мне написать функцию from, второй способ позволяет мне написать функцию to, а третий способ кажется бесполезным.

Есть ли способ определить оболочку, чтобы я мог реализовать весь экземпляр Generic?

Просто укажите дополнительную переменную как (), она в любом случае фантомная (если вы не используете Generic1, но это кажется маловероятным). Но если F - это семейство типов, вам должны понадобиться экземпляры ToJSON только для тех типов, которым они сопоставляются.

Li-yao Xia 11.04.2018 15:25

@ Li-yaoXia Невозможно реализовать toили жеfrom, если вы просто укажете его как (). Кроме того, семейство типов отображает представление в почти идентичное представление, но с полями (в конструкторах «K1»), оболочкой в ​​Maybe. Экземпляры JSON для общих типов реплик отсутствуют, или, по крайней мере, их экземпляры не то, что мне нужно.

Luka Horvat 11.04.2018 15:51

Хорошо, спасибо, я лучше понимаю, в чем проблема.

Li-yao Xia 11.04.2018 16:10
1
3
41
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Учитывая тип данных A, Generic дает изоморфизм универсальному представлению Rep A. Здесь мы хотим пойти другим путем. Учитывая общее представление r (здесь r ~ F (Rep A) для некоторого A), нам нужен тип данных B, такой как Rep B ~ r. Мы определяем B как Data r:

data Data r = Data (r ())

Параметр типа r :: * -> * - фантом (на самом деле он используется Generic1, и никто не использует Generic1), поэтому мы можем создать его экземпляр с чем угодно, и мы выбираем () как произвольное значение по умолчанию.

Нам нужно будет выразить эту фантомность как ограничение. Один из способов - использовать комбинацию Functor и Contravariant.

type Bivariant f = (Functor f, Contravariant f)

phantom :: Bivariant f => f a -> f b  -- Data.Functor.Contravariant

instance Bivariant r => Generic (Data r) where
  type Rep (Data r) = r
  from (Data r) = phantom r
  to = Data . phantom

Также я определил Data в общие данные, где я сохранил дополнительный параметр вместо его создания, поскольку на самом деле это дешево.

Ха! phantom - хороший трюк. Надеюсь, что вскоре с количественными контекстами мы сможем потребовать прямого принуждения.

Luka Horvat 11.04.2018 16:57

Да, так будет намного приятнее!

Li-yao Xia 11.04.2018 17:08

Другие вопросы по теме