【问题标题】:How to determine if an expression passed to a macro will always result in the same value?如何确定传递给宏的表达式是否总是产生相同的值?
【发布时间】:2015-07-27 13:16:15
【问题描述】:

假设我定义了一个宏,如下所示。它本质上是输入一个 T 类型的表达式并返回一个 MyType[T] 类型的对象(所涉及的实际类型并不重要)。

object MyMacro {
  def macroImpl[T : context.WeakTypeTag, U : context.WeakTypeTag](context : scala.reflect.macros.blackbox.Context) (expression : context.Expr[T]) : context.Expr[U] =
}

object MyObj {
  def callMacro[T](expression : T) : MyType[T] = macro MyMacro.macroImpl[T, MyType[T]]
}

在我的宏中,我想确定传递的表达式是否为常量。我的意思是,我想知道表达式在运行时评估后是否可以随后评估为不同的值。如果它是恒定的,我可以应用某些非常有用的优化。

我知道一个表达式是常量,如果它是:

  • 文字表达式。
  • “this”表达式。
  • 对 val 或参数的引用。
  • 对象表达式为常量且被调用的成员是 val 或惰性 val 的成员调用。

例如,下面前五次调用 callMacro 时传递的表达式应被视为常量:

class MyClass {
  val i = 0

  val myLiteral = callMacro("Hi!") //constant - literal expression

  val myThis = callMacro(this) //constant - this expression

  val myInt = callMacro(i) //constant - reference to a val

  def myMethod(p : MyOtherClass) {
    val myParameter = callMacro(p) //constant - reference to a parameter
    val myValMember = callMacro(p.x) //constant - invocation of val member
    val myVarMember = vallMacro(p.y) //NOT constant - invocation of var member
    val myVarMember = vallMacro(p.z) //NOT constant - invocation of def member
  }
}

class MyOtherClass(val x : Int, var y : Int) {
  def z = x + y
}

我已经为前两种情况实现了代码(这很简单)。

def isConstant[T](context : scala.reflect.macros.blackbox.Context) (expression : context.Expr[T]) = {
  import context.universe._
    expression.tree match {
      case This(_) =>
        true
      case Literal(_) =>
        true
      /*...put additional cases here...*/
      case _ =>
        false
  }
}

但是,我不确定这样的东西是否已经存在,或者是否有可能检测到在对象上调用的成员是否是 val。

是否可以实现第四个标准?或者,API 中是否已经存在类似的内容?

【问题讨论】:

    标签: scala scala-macros


    【解决方案1】:

    我想出了一个解决办法。基本上归结为我不知道比例反射系统中的符号。

    我最终添加了第五个条件来处理引用隐式参数或对象的情况。

    implicit class extendSymbol(symbol : scala.reflect.macros.blackbox.Context#Symbol) {
      def isStable =
        (symbol.isTerm && symbol.asTerm.isStable) || (symbol.isMethod && symbol.asMethod.isStable)
    }
    
    def isConstant[T](context : scala.reflect.macros.blackbox.Context) (tree : context.Tree) : Boolean = {
      import context.universe._
      tree match {
        case This(_) =>
          true
        case Literal(_) =>
          true
        case ident @ Ident(_) =>
          ident.symbol.isStable
        case select @ Select(objExpr, term) =>
          isConstant(context) (objExpr) && select.symbol.isStable
        //for implicit values
        case Apply(TypeApply(Select(Select(This(TypeName("scala")), TermName("Predef")), TermName("implicitly")), _), _) =>
          true
        case _ =>
          false
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-10
      • 1970-01-01
      相关资源
      最近更新 更多