Template Haskell имеет следующие новые синтаксические конструкции. Вам необходимо использовать расширение TemplateHaskell, чтобы включить эти синтаксические расширения. В качестве альтернативы, расширение TemplateHaskellQuotes может быть использовано для включения подмножества цитирования Template Haskell (т.е. без вставок на верхнем уровне). Расширение TemplateHaskellQuotes считается безопасным в Безопасном Haskell, в то время как TemplateHaskell нет.
-
Вставка записывается $x, где x — произвольное выражение. Между символом “$” и выражением не должно быть пробела. Это использование $ переопределяет его значение как инфиксного оператора, так же как M.x переопределяет значение . как инфиксного оператора. Если вам нужен инфиксный оператор, поставьте пробелы вокруг него.
Вставка на верхнем уровне может быть расположена вместо
- выражения; вставленное выражение должно иметь тип
Q Exp
- шаблона; вставленный шаблон должен иметь тип
Q Pat
- типа; вставленное выражение должно иметь тип
Q Type
- списка объявлений на верхнем уровне; вставленное выражение должно иметь тип
Q [Dec]
Внутри вставки вы можете только вызывать функции, определенные в импортированных модулях, а не функции, определенные в другом месте в том же модуле. Обратите внимание, что вставки объявлений не допускаются нигде, кроме верхнего уровня (вне других объявлений).
Монада Q — это монад, определенная в Language.Haskell.TH.Syntax, которая поддерживает несколько полезных операций во время генерации кода, таких как сообщение об ошибках или поиск идентификаторов в среде.
-
Цитата выражения записывается в квадратных скобках, таким образом:
-
[| ... |], или [e| ... |], где “…” — это выражение; цитата имеет тип Quote m => m Exp. -
[d| ... |], где “…” — это список объявлений верхнего уровня; цитата имеет тип Quote m => m [Dec]. -
[t| ... |], где “…” — это тип; цитата имеет тип Quote m => m Type. -
[p| ... |], где “…” — это шаблон; цитата имеет тип Quote m => m Pat.
Класс типов Quote (Language.Haskell.TH.Syntax.Quote) — это минимальный интерфейс, необходимый для реализации разбора цитат. Монада Q является экземпляром Quote, но содержит много других операций, которые не нужны для определения цитат.
См. Где они могут быть? для использования частичных сигнатур типов в цитатах.
-
Вставки могут быть вложены внутри скобок цитирования. Например, фрагмент, представляющий 1 + 2, может быть построен с использованием вложенных вставок:
oneC, twoC, plusC :: Quote m => m Exp
oneC = [| 1 |]
twoC = [| 2 |]
plusC = [| $oneC + $twoC |]
-
Точный тип цитаты зависит от типов вложенных вставок внутри нее:
-- Add a redundant constraint to demonstrate that constraints on the
-- monad used to build the representation are propagated when using nested
-- splices.
f :: (Quote m, C m) => m Exp
f = [| 5 | ]
-- f is used in a nested splice so the constraint on f, namely C, is propagated
-- to a constraint on the whole representation.
g :: (Quote m, C m) => m Exp
g = [| $f + $f |]
Помните, что вставка на верхнем уровне все равно требует, чтобы ее аргумент был типа Q Exp. Так что вставка в g приведет к тому, что m будет инстанцирован в Q.
h :: Int
h = $(g) -- m ~ Q
-
Типизированная вставка выражения записывается $$x, где x — произвольное выражение.
Типизированная вставка выражения на верхнем уровне может быть расположена вместо выражения; вставленное выражение должно иметь тип Code Q a.
ПРИМЕЧАНИЕ: В настоящее время типизированные вставки могут препятствовать предупреждению об использовании неиспользуемых идентификаторов для идентификаторов в области видимости. См. #16524.
-
Типизированная цитата выражения записывается как [|| ... ||], или [e|| ... ||], где “…” — это выражение; если выражение “…” имеет тип a, то цитата имеет тип Quote m => Code m a.
Можно извлечь значение типа m Exp из Code m a с помощью функции unTypeCode :: Code m a -> m Exp.
-
Квазицитата может появиться в контексте шаблона, типа, выражения или объявления и также записывается в квадратных скобках:
-
Имя можно процитировать с одним или двумя префиксами одинарных кавычек:
-
'f имеет тип Name, и называет функцию f. Аналогично 'C имеет тип Name и называет конструктор данных C. В общем '⟨thing⟩ интерпретирует ⟨thing⟩ в контексте выражения.
Имя, второй символ которого является одинарной кавычкой, не может быть процитирован таким образом, потому что оно будет обработано как литерал символа. Например, если функция называется f'7 (что является допустимым идентификатором Haskell), попытка процитировать ее как 'f'7 будет обработана как литерал символа 'f' и числовой литерал 7. Что касается продвинутых конструкторов (Различие между типами и конструкторами), обходной путь заключается в добавлении пробела между кавычкой и именем. Имя функции f'7 записывается как ' f'7.
-
''T имеет тип Name, и называет конструктор типа T. То есть, ''⟨thing⟩ интерпретирует ⟨thing⟩ в контексте типа.
Эти Names могут быть использованы для построения выражений, шаблонов, объявлений Template Haskell и т.д. Они также могут быть переданы в качестве аргумента функции reify.
-
Возможна ситуация, когда вставка расширяется до выражения, содержащего имена, которые не находятся в области видимости в месте вставки. Рассмотрим следующий код:
module Bar where
import Language.Haskell.TH
add1 :: Quote m => Int -> m Exp
add1 x = [| x + 1 |]
Теперь рассмотрим вставку, использующую add1 в отдельном модуле:
module Foo where
import Bar
two :: Int
two = $(add1 1)
Template Haskell не может знать, каким будет аргумент к add1 в месте определения функции, поэтому используется механизм подъема для продвижения x до значения типа Quote m => m Exp . Эта функциональность предоставляется пользователю как класс типов Lift в модуле Language.Haskell.TH.Syntax. Если у типа есть экземпляр Lift , то любое его значение может быть поднято до выражения Template Haskell:
class Lift t where
lift :: Quote m => t -> m Exp
liftTyped :: Quote m => t -> Code m t
В общем случае, если GHC видит выражение в квадратных скобках (например, [|
foo bar |], то GHC ищет каждое имя в скобках. Если имя является глобальным (например, предположим, что foo получено из импорта или объявления верхнего уровня), то используется полное имя. Если имя является локальным (например, предположим, что bar связано локально в определении функции mkFoo bar = [| foo bar |] ), то GHC использует lift (так GHC имитирует, что [| foo bar |] на самом деле содержит [|
foo $(lift bar) |]). Локальные имена, которые не находятся в области видимости в местах вставок, фактически вычисляются при обработке цитаты.
Библиотека template-haskell предоставляет экземпляры Lift для многих распространенных типов данных. Кроме того, можно автоматически вывести экземпляры Lift с помощью расширения языка DeriveLift. См. Вывод экземпляров Lift для получения дополнительной информации.
-
Вы можете опустить $(...) в вставке объявления верхнего уровня. Простая запись выражения (а не объявления) подразумевает вставку. Например, вы можете написать
module Foo where
import Bar
f x = x
$(deriveStuff 'f) -- Uses the $(...) notation
g y = y+1
deriveStuff 'g -- Omits the $(...)
h z = z-1
Это сокращение делает вставки объявлений верхнего уровня более тихими и менее пугающими.
-
Вставки шаблонов вводят связывающие переменные, но область видимости переменных в выражениях внутри области видимости шаблона проверяется только при выполнении вставки. Обратите внимание, что вставки шаблонов, которые появляются вне скобок цитирования, выполняются во время компиляции. Вставки шаблонов, появляющиеся внутри скобок цитирования, не выполняются во время компиляции; они выполняются при вставке скобок, в какой-то момент позже. Например,
mkPat :: Quote m => m Pat
mkPat = [p| (x, y) |]
-- in another module:
foo :: (Char, String) -> String
foo $(mkPat) = x : z
bar :: Quote m => m Exp
bar = [| \ $(mkPat) -> x : w |]
будет завершено ошибкой, так как z находится вне области видимости в определении foo, но это не завершится ошибкой, так как w находится вне области видимости в определении bar. Это произойдет только при вставке bar.
-
Квазицитата шаблона может генерировать связывающие переменные, которые охватывают правую часть определения, так как эти связывающие переменные находятся в области видимости лексически. Например, задана квазицитата haskell , которая анализирует Haskell, в следующем коде имя y в правой части f относится к имени y , связанному шаблоном квазицитаты haskell, а не глобальному имени y = 7.
y :: Int
y = 7
f :: Int -> Int -> Int
f n = \ [haskell|y|] -> y+n