Haskell - манипулирование/расширение ADT, который не под вашим контролем

Каков наилучший способ манипулирования/расширения ADT, который не под вашим контролем? (т.е. от зависимости)

Здесь - тип данных, относящийся к моей проблеме:

Я хочу сохранить структуру данных, но добавлять дополнительные данные (например, добавить другой тип), но сама структура не находится под моим контролем. Должен ли я сопоставлять данные с моей собственной версией этого определения?

Например, для всех абзацев в структуре я хотел бы, чтобы Para стал Para [Inline] [String], где [String] - это список слов, содержащихся в параграфе (как собственная структура данных).

Я обслуживаю эти данные как JSON через конечную точку, я думал, что один из способов, которыми я мог бы обойти это, - определить мой собственный экземпляр ToJSON и выполнить этот перевод на Para там, однако я не могу переопределить экземпляр так, как он уже определен! Я согласен принять решение, которое на самом деле не касается самого типа Para, мне просто нужен способ связать больше данных с Para, не теряя при этом никакой структуры полного документа Pandoc.

Ответ 1

однако я не могу переопределить экземпляр, поскольку он уже определен!

Вы можете определить newtype, который обертывает Pandoc, и затем определить для него специальный экземпляр ToJSON. query из Text.Pandoc.Walk может легко и эффективно извлекать строки из Пара.

Еще одна вещь, которую вы могли бы рассмотреть, - создать функцию, которая обертывает каждую Para внутри Div со строками, хранящимися в одном из его атрибутов. Из того, что вы сказали, неясно, будет ли это работать для ваших целей, но было бы легко определить функцию, которая сделала это преобразование.

Ответ 2

Да, я думаю, что определение вашего сериализатора JSON, вероятно, лучший способ. Вы можете определить сериализаторы JSON без использования механизма typeclass, потому что Aeson хорошо разработан (спасибо @bos), просто определите функцию из Block -> Value. К сожалению, вам кажется, что вам придется проходить каждый случай вручную, по крайней мере, в случаях с вложенными Block, такими как BlockQuote, OrderedList и т.д. Для остальных вы можете просто переслать ToJSON:

serialize :: Block -> Value
serialize (BlockQuote bs) = object
   [ "type" .= "blockquote"       -- or whatever the encoding is
   , "blocks" .= map serailize bs
   ]
...  -- implement this for every constructor with recursive Blocks
serialize b = toJSON b

Это не здорово, так как вам, возможно, придется напрямую переписать написанные вещи. Я не вижу пути вокруг него, учитывая дизайн pandoc (хотя часто АСТ параметризуются по типу аннотаций, например haskell-src-exts, или использовать некоторый дизайн с открытой фиксированной точкой, что позволило бы сделать что-то более умное).

Достаточно грубым способом было бы сериализовать с помощью ToJSON, найти только те части структуры JSON, которые вы хотите аннотировать, десериализировать только эту часть и вычислить аннотацию, повторно инициализировать, а затем добавить свою вычисленную аннотацию. Очень уродливо, но вам, разумеется, не нужно переопределять сериализаторы. Если у pandoc было много рекурсивных конструкторов или очень сложная сериализация, я мог бы подумать об этом, но, поскольку он стоит, я бы, вероятно, просто укусил пулю и повторил рекурсивные случаи.