模板 Haskell 和 Quasiquotes 的语法

模板 Haskell 由 -XTemplateHaskell GHC 扩展启用。此扩展启用了本节中进一步详述的所有语法功能。有关 Template Haskell 的完整详细信息,请参见用户指南

接头

  • splice 是一个由 Template Haskell 启用的新语法实体,写为 $(...),其中 (...) 是一个表达式。

  • $ 和表达式的第一个字符之间不能有空格; 和模板 Haskell 覆盖了 $ 运算符的解析 - 例如,f$g 通常被解析为 ($) f g,而启用了 Template Haskell,它被解析为拼接。

  • 当拼接出现在顶层时,可以省略 $。在这种情况下,拼接表达式是整行。

  • splice 表示在编译时运行以生成 Haskell AST 的代码,并且该 AST 被编译为 Haskell 代码并插入到程序中

  • 拼接可以代替:表达式,模式,类型和顶级声明。在每种情况下,拼接表达的类型分别是 Q ExpQ PatQ TypeQ [Decl]。请注意,声明拼接可能出现在顶层,而其他拼接可能分别出现在其他表达式,模式或类型中。

表达式引用(注意: 不是 QuasiQuotation)

  • 表达式引用是一个新的语法实体,写成以下之一:

    • [e|..|][|..|] - .. 是一个表达式,引用的类型为 Q Exp;
    • [p|..|] - .. 是一种模式,引用的类型为 Q Pat;
    • [t|..|] - .. 是一个类型,引用的类型为 Q Type;
    • [d|..|] - .. 是一个声明列表,引用的类型为 Q [Dec]
  • 表达式引用采用编译时程序并生成该程序表示的 AST。

  • 在没有拼接的情况下在引用中使用值(例如\x -> [| x |])对应于\x -> [| $(lift x) |] 的语法糖,其中 lift::Lift t => t -> Q Exp 来自类

    class Lift t where
      lift::t -> Q Exp
      default lift::Data t => t -> Q Exp

键入的拼接和引用

  • 类型拼接与之前提到的(无类型)拼接类似,并且写为 $$(..),其中 (..) 是表达式。

  • 如果 e 的类型为 Q (TExp a),则 $$e 的类型为 a

  • 输入的引文采用 [||..||] 的形式,其中 ..a 类型的表达式; 结果引号的类型为 Q (TExp a)

  • 键入的表达式可以转换为无类型的表达式:unType::TExp a -> Exp

QuasiQuotes

  • QuasiQuotes 概括了表达式引用 - 之前,表达式引用使用的解析器是固定集(e,p,t,d)之一,但 QuasiQuotes 允许定义自定义解析器并在编译时使用它来生成代码。准引用可以出现在与常规引用相同的上下文中。

  • 准引用被写为 [iden|...|],其中 idenLanguage.Haskell.TH.Quote.QuasiQuoter 类型的标识符。

  • QuasiQuoter 简单地由四个解析器组成,每个解析器可以出现一个不同的上下文:

    data QuasiQuoter = QuasiQuoter { quoteExp  :: String -> Q Exp,
                                     quotePat  :: String -> Q Pat,
                                     quoteType::String -> Q Type,
                                     quoteDec  :: String -> Q [Dec] }

名称

  • Haskell 标识符由 Language.Haskell.TH.Syntax.Name 类型表示。名称形成抽象语法树的叶子,表示模板 Haskell 中的 Haskell 程序。

  • 当前在范围内的标识符可以变为具有以下任一名称的名称:'e'T。在第一种情况下,e 在表达式范围内被解释,而在第二种情况下,T 在类型范围内(回想一下类型和值构造函数可以在 Haskell 中没有歧义地共享名称)。