Шаблон Haskell имеет две основные операции:
- подъем:
[| |]
- сращивание
$( )
Когда вы заключаете что-то в скобки Оксфорда, вы задерживаете проверку (и оценку) его типа и вместо этого создаете фрагмент AST, который будет проверен на тип, когда он будет вставлен обратно.
АСТ, который построен, можно наблюдать:
{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
main = print =<< runQ [| "hello" + 1 |]
Запуск этой программы (или ввод выражения в скобках в GHCi), и мы получаем правильно сформированный AST, но тот, который не является корректным по типу, если рассматривается как фрагмент Haskell:
InfixE (Just (LitE (StringL "hello"))) (VarE GHC.Num. +) (Just (LitE (IntegerL 1)))
Теперь, когда мы пытаемся на самом деле соединить его, происходит проверка типа:
*Main> :t [| "hello" + 1 |]
[| "hello" + 1 |] :: Q Exp
*Main> $( [| "hello" + 1 |] )
<interactive>:1:4:
No instance for (Num [Char])
arising from the literal `1'
Как мы и ожидаем. Так что да, выражения TH проверяются типом, но в более поздний момент, когда они вставляются обратно в программу.