如何将参数/设置传递给Scala宏?

时间:2014-08-04 20:39:18

标签: scala parameter-passing scala-macros

如何将参数/设置传递给Scala宏?

这些设置不应该是全局的,而是每次宏调用。

我想要的是类似的东西:

def a(param: Int) = macro internalMacro("setting 1")
def b(param: Int) = macro internalMacro("setting 2")

然后setting 1setting 2应该是常量值,可以在宏中访问,因此我可以使内部行为依赖于它们。

1 个答案:

答案 0 :(得分:12)

您的方法和宏定义的参数列表必须准确对齐,宏定义方法具有上下文的额外初始参数列表,并且方法定义中的每个其他参数具有相同的名称和相同的类型包裹在c.Expr中。 (但请注意,类型参数列表可能不同。)

这意味着您无法将信息作为方法定义中的参数传递给宏实现。你可以使用静态注释来完成同样的事情(这是我从Eugene Burmako那里学到的一个技巧,他使用它来实现没有反射访问的结构类型,a.k.a。vampire methods):

import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context

class setting(value: String) extends StaticAnnotation

def internalMacro(c: Context)(param: c.Expr[Int]) = {
  import c.universe._
  val settingValue = c.macroApplication.symbol.annotations.filter(
    _.tree.tpe <:< typeOf[setting]
  ).headOption.flatMap(
    _.tree.children.tail.collectFirst {
      case Literal(Constant(s: String)) => s
    }
  ).getOrElse(
    c.abort(c.enclosingPosition, "Annotation body not provided!")
  )
  settingValue match {
    case "setting 1" => c.Expr(q"42")
    case _ => param
  }
}

然后:

scala> @setting("setting 1") def a(param: Int): Int = macro internalMacro
defined term macro a: (param: Int)Int

scala> @setting("setting 2") def b(param: Int): Int = macro internalMacro
defined term macro b: (param: Int)Int

scala> def c(param: Int): Int = macro internalMacro
defined term macro c: (param: Int)Int

scala> a(10)
res0: Int = 42

scala> b(10)
res1: Int = 10

scala> c(10)
<console>:22: error: Annotation body not provided!
              c(10)
               ^

我甚至没有检查任何封闭的树木。请参阅我的博客文章here,了解此方法的实例。