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