HaTeXExtension + TH: Спаривание операторов
(репост из tumblr)
Продолжаю серию дополнений HaTeXExtension с использованием Template Haskell. В этом посте будет пример шаблона с рекурсивным сплайсингом/цитированием.
Кроме отдельных операторов-синонимов, о которых шла речь в предыдущем посте, я определяю иногда их спаренный вариант - это удобно, потому что иногда хочется написать их один за другим, а поотдельности нельзя (каждому из них нужен второй аргумент), а если написать их слитно, то два операторных символа подряд с синтаксической точки зрения - просто новое имя оператора. Вот пример того, что есть сейчас:
1 2 3 |
|
Хорошо, от первых двух деклараций мы избавились изменив шаблон defTeXCommand
. Теперь я хочу сделать шаблон, который будет брать пары операторных символов и генерировать объявление их спаренного варианта. Гм, пошёл делать и понял, что можно брать не пары а списки, чтобы можно было сплавлять сколько угодно операторов.
(…прошло некоторое время…)
В общем я подумал, потыкался, и не придумал, как сделать этот шаблон, чтобы передавать ему сами операторы, потому что непонятно, как имея лишь функцию, получить её имя.. Подумаю ещё потом, а пока сделаю шаблон, который будет брать имена операторов в виде строк и делать своё не гигиеничное спаривание “)
1 2 3 4 5 6 7 8 9 10 |
|
Чтобы не писать [["¸","⎕","≤"], ["¸","…","¸"]]
, что практически не читабельно, мы будем писать так: ["¸ ⎕ ≤", "¸ … ¸"]
, а в шаблоне эти строки просто разбиваются по пробелам на списки отдельных имён.
Итак, разберём это определение. Шаблон берёт такой вот список сгруппированных имён операторов, пробегает по нему и создаёт объявления операторов со склеенными именами (concat ops
). Тело каждого такого оператора выглядит как лямбда λ a b → a ◇ $(fuse ops) ◇ b
, то есть оператор, берёт два аргумента и вклеивает между ними спаренные операторы.
Самое интересное - это fuse
. Она берёт список операторов (строк с их именами), и делает рекурсивную подстановку. Напомню, что dyn x
- это процитированный оператор, мы его сплайсим, то есть вклеиваем и даём ему два пустых аргумента (у него ведь тип LaTeX → LaTeX → LaTeX
), чтобы получить просто значение типа LaTeX
. А дальше мы соединяем (◇
) это с вклеенным результатом рекурсивного вызова $(fuse xs)
. Когда же операторы заканчиваются, fuse []
возвращает процитированную пустую строку (на самом деле это значение TeXEmpty
).
Обычная рекурсивная функция, по своей сути ничем не примечательна, но интересно это цитирование и расцитирование, которое делается по ходу дела. Нужно внимательно следить, чтобы не процитировать два раза или не сделать дважды сплайсинг - баланс сил сохраняй, юный хаскеллер “)