Данный текст является переводом документации 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
, который разбирается в конце.