scala - 将类型参数用作传递给宏实现的另一种类型的类型参数时出现类型错误

标签 scala scala-macros

我正在尝试创建一个宏,它允许我捕获传递给构造函数的表达式的文本。我想要表达式的文本用于调试目的。宏实现如下:

package nimrandsLibrary.react.macroImpl

object Macros {
    def applyImpl[T : context.WeakTypeTag, U : context.WeakTypeTag](context : scala.reflect.macros.Context) (expression : context.Expr[T]) : context.Expr[U] = {
        import context.universe._
        context.Expr[U](context.universe.New(context.universe.weakTypeOf[U], expression.tree, context.universe.Literal(context.universe.Constant(expression.tree.toString()))))
    }
}

定义如下:

class Signal(expression : => T, expressionText : String) {
    ...
}

object Signal {
    def apply[T](expression : T) = macro nimrandsLibrary.react.macroImpl.Macros.applyImpl[T, Signal[T]]
}

但是,无论我在哪里调用它,都会出现错误。

val mySignal = Signal{ 2 }  //type mismatch; found : Int required : T

但是,当然,T 的类型是 Int,所以错误没有意义。

似乎在宏扩展中编译器以某种方式忘记了用 Signal[Int] 替换 Signal[T]。作为实验,我尝试更改定义站点,以便必须提供两种类型,如下所示:

def apply[T, U](expression : T) = macro nimrandsLibrary.react.macroImpl.Macros.applyImpl[T, U]

然后,我这样调用它:

Signal[Int, Signal[Int]]{ 2 }

而且,那行得通。但是,当然,这根本不是我想要的语法。这是一个错误,还是我以某种方式做错了?有解决方法吗?

最佳答案

问题是宏实现中 U 的弱类型标签的参数实际上是符号 T,而不是 Int .

以下将起作用(请注意,为了清楚起见,我缩短了一些名称并替换了已弃用的方法):

import scala.language.experimental.macros
import scala.reflect.macros.Context

object Macros {
  def applyImpl[
    T: c.WeakTypeTag,
    U: c.WeakTypeTag
  ](c: Context)(e: c.Expr[T]): c.Expr[U] = {
    import c.universe._

    c.Expr[U](
      Apply(
        Select(
          New(
            TypeTree(
              appliedType(weakTypeOf[U].typeConstructor, weakTypeOf[T] :: Nil)
            )
          ),
          nme.CONSTRUCTOR
        ),
        List(e.tree, Literal(Constant(e.tree.toString)))
      )
    )
  }
}

class Signal[T](val expression: T, val expressionText: String)

object Signal {
  def apply[T](e: T) = macro Macros.applyImpl[T, Signal[T]]
}

然后:

scala> Signal(1).expressionText
res0: String = 1

正如预期的那样。


迈尔斯·萨宾 points out on Twitter ,如果 U 是一个类型构造函数会更好,因为在上面的版本中,宏实现对类型中未捕获的 U 做了一些假设。以下是在这方面更安全的方法:

import scala.language.experimental.macros
import scala.language.higherKinds
import scala.reflect.macros.Context

object Macros {
  def applyImpl[
    T: c.WeakTypeTag,
    U[_]
  ](c: Context)(e: c.Expr[T])(implicit u: c.WeakTypeTag[U[_]]): c.Expr[U[T]] = {
    import c.universe._

    c.Expr[U[T]](
      Apply(
        Select(
          New(
            TypeTree(
              appliedType(u.tpe.typeConstructor, weakTypeOf[T] :: Nil)
            )
          ),
          nme.CONSTRUCTOR
        ),
        List(e.tree, Literal(Constant(e.tree.toString)))
      )
    )
  }
}

class Signal[T](val expression: T, val expressionText: String)

object Signal {
  def apply[T](e: T) = macro Macros.applyImpl[T, Signal]
}

请特别注意 SignalapplyImpl 的第二个参数的变化。

关于scala - 将类型参数用作传递给宏实现的另一种类型的类型参数时出现类型错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19378620/

相关文章:

java - 如何设置 script-src 'self' 'unsafe-inline' 'unsafe-eval' ;样式-src 'self' 'unsafe-inline' ;对于 Play Framework 2.6.x 中的特定页面或路径?

scala - 如何调用带有Tuple2的2个参数的函数?

Scala 宏和单独的编译单元

scala - 我该如何将 "this"封装在Scala宏中?

list - 如何将Vector设置为默认实现?

scala - VisualVM、Scala 和 Ubuntu?

scala - DSL 提取案例类字段名称

Scala 宏检查匿名函数的树

scala - 如何键入检查 Def Def

scala - 蛋糕图案嵌套特征