Scala 宏:从宏发出理解

Scala macros: Emit for comprehensions from macros

试图从黑盒宏发出一个 for yield 块,但我无法理解如何使用有效语法创建块。

因此 source 下面是一个硬编码的参数名称,因为此块稍后将插入到将具有匹配参数名称的方法中。 params 只是 params: Seq[c.universe.ValDef],包含 case class 个字段。

def extract(source: Source): Option[CaseClass] = { ... }

val extractors = accessors(c)(params) map {
  case (nm, tpe) => {
    val newTerm = TermName(nm.toString + "Opt")
    q"""$newTerm <- DoStuff[$tpe].apply("$nm", source)"""
  }
}

val extractorNames = accessors(c)(params) map {
  case (nm, tpe) => TermName(nm.toString + "Opt")
}

这基本上是采用 case class,并输出一个 for yield black,基本上从理解中重新创建案例 class。

name: Type 形式的 case class 中的每个字段都被转换为一组提取器,如果 for 理解成功,这些提取器会返回相同的 case class 实例。

case class Test(id: Int, text: String) 

将被宏转换为以下内容,其中 Extract 只是一种类型 class 而 Extract.apply[T : Extract] 只是具体化与 implicitly[Extract[T]] 绑定的上下文:

for {
  idOpt <- Extract[Int].apply("id", source): Option[Int]
  textOpt <- Extract[String].apply("text", source): Option[String]
} yield Test(idOpt, textOpt)

问题在于必须引用内部的 yield 表达式并输出 a <- b 块。

def extract(source: Source): Option[$typeName] = {
  for {(..$extractors)} yield $companion.apply(..$extractorNames)
}

错误是 ';' expected but '<-' found,这很明显,因为 a <- b 本身是无效的 Scala。生成和准引用表达式块的正确方法是什么才能使上述内容起作用?

Here 是所有不同种类的准引号的列表。 在那里您可以看到,要表达 a <- b 语法,您需要 fq 插值器。 所以该代码可能会变成:

val extractors = accessors(c)(params) map {
  case (nm, tpe) => {
    val newTerm = TermName(nm.toString + "Opt")
    fq"""$newTerm <- DoStuff[$tpe].apply("$nm", source)"""
  }
}

然后使用普通插值器:

q"for (..$extractors) yield $companion.apply(..$extractorNames)"