在Scala宏中访问参数值时的编译问题

时间:2013-08-29 19:33:50

标签: scala reflection scala-macros

此最小示例的调用代码似乎是编译(Eclipse Indigo SR2,Scala v2.10.20),但包含调用代码的项目标有红叉(我不清楚如何进一步诊断) 。没有生成类文件。如果我用一些文字替换param.value,例如1,调用代码编译。

这是一个已知问题吗?有解决方法吗?

def myMacro( param : Int ): Int = macro myMacroImpl( param )

def myMacroImpl(c: Context)(param: c.Expr[Int]): c.Expr[Int] = {
    import c.universe._

    c.Expr( Literal( Constant( param.value ) ) )         
}

1 个答案:

答案 0 :(得分:3)

首先,宏定义的语法不需要(或允许)指定宏实现方法的参数,因此您的代码永远不应该编译,即使在实现中使用文字也是如此。请参阅下面的示例,了解正确的语法。

接下来,Expr.value不幸是谎言。想想宏方法的参数是变量的情况。变量的值不是(并且在一般情况下不能)在编译时已知,但是您尝试使用该值创建编译时文字常量。它从根本上说不起作用。

您有几个选项,可能适用也可能不适用,具体取决于您要执行的操作。例如,假设我们需要在param值中添加一个并返回结果。如果我们希望在编译时添加,那么当我们没有获得编译时文字时,我们必须抛出(编译时)异常:

def myMacro(param: Int): Int = macro myMacroImpl

def myMacroImpl(c: Context)(param: c.Expr[Int]): c.Expr[Int] = {
  import c.universe._

  val p = param.tree match {
    case Literal(Constant(p: Int)) => p
    case _ => c.abort(c.enclosingPosition, "param must be a literal value!")
  }

  c.literal(p + 1) // Or, equivalently: c.Expr(Literal(Constant(p + 1))
}

另一种选择是围绕param表达式构建树。例如,以下内容还将后继返回到param,但添加是在运行时执行的,并且该方法可以处理非文字参数:

def myMacro(param: Int): Int = macro myMacroImpl

def myMacroImpl(c: Context)(param: c.Expr[Int]): c.Expr[Int] = {
  import c.universe._

  c.Expr(
    Apply(Select(param.tree, newTermName("$plus")), c.literal(1).tree :: Nil)
  )
}

但是,我们不会同时获得编译时添加和任意param表达式。