Данный текст является переводом документации Template Haskell, написанной Булатом Зиганшиным. Перевод всего текста разбит на несколько логических частей для облегчения восприятия. Далее курсив в тексте — примечания переводчика. Предыдущие части:
Материализация
Материализация (reification) — это средство Template Haskell, позволяющее программисту получить информацию из таблицы символов компилятора. Монадическая функция
reify ∷ Name → Q Info возвращает информацию о данном имени: если это глобальный идентификатор (функция, константа, конструктор) – вы получите его тип, если это тип или класс – вы получите его структуру. Определение типа
Info можно найти в модуле
Language.Haskell.TH.Syntax.
Материализация может быть использована для того, чтобы получить структуру типа, но таким образом нельзя получить тело функции. Если вам нужно материализовать тело функции, то определение функции нужно процитировать и дальше можно будет работать с этим определением с помощью другого шаблона. Например так:
$(optimize [d| fib = … |])
или так
fib = $(optimize [| … |])
На самом деле, в оригинальной статье больше ничего не говорится про материализацию. Не знаю, насколько это содержательная тема – необходимый минимум знаний о ней ограничивается функцией reify и типом Info, но есть некоторые тонкости, связанные например с тем, что можно получить информацию не о любом имени. Если эта тема интересна, я могу собрать какую-нибудь информацию и написать об этом отдельную заметку (или вклеить сюда).
Облегчённое цитирование имён
Чтобы получить имя (
∷ Name), соответствующее интересующему идентификатору, можно использовать функцию
mkName, но это не безопасное решение, потому что
mkName возвращает не квалифицированное имя, которое может интерпретироваться по-разному в зависимости от контекста. А вот код
VarE id ← [| foo |] безопасен в этом смысле, так как цитирование квалифицирует имена (получится что-то типа
My.Own.Module.foo), но этот код слишком многословный и требует монадический контекст для использования. К счастью, Template Haskell, имеет другую простую форму цитирования имён:
'foo (одинарная кавычка перед
foo) имеет тип
Name и содержит квалифицированное имя, соответствующее идентификатору
foo, так что код
let id = 'foo эквивалентен по смыслу коду
VarE id ← [| foo |]. Обратите внимание, что эта конструкция имеет простой тип
Name (а не
Q Exp или
Q Name), так что она может быть использована там, где не возможно использование монад, например:
f ∷ Exp → Exp
f (App (Var m) e) | m == 'map = …
Эта новая форма тем не менее является цитированием и подчиняется тем же правилам, что и цитирующие скобки
[| … |]. Например, она не может быть использована внутри этих скобок (так нельзя:
[| 'foo |]),
но и вклеивание к ней не может быть применено (так тоже нельзя: $( 'foo )), потому что для вклейки нужен тип Q …. Более важно то, что эта форма определяется статически, возвращая полностью квалифицированное имя, с однозначной интерпретацией.
Haskell’евские пространства имён немного всё усложняют. Цитата
[| P |] означает конструктор данных
P, в то время как
[t| P |] означает конструктор типа
P. Поэтому для “облегчённого цитирования” необходим такой же способ разделения этих сущностей. Для контекста типов используется просто две одинарные кавычки:
'Foo означает “конструктор данных Foo в контексте выражения”
'foo означает “имя foo в контексте выражения”
''Foo означает “конструктор типа Foo в контексте типов”
''foo означает “переменная типа foo в контексте типов”
Облегчённая форма цитирования используется в примере генерации воплощений класса
Show, который разбирается в конце.