我正在尝试创建一个宏,它允许我捕获传递给构造函数的表达式的文本。我希望表达式的文本用于调试目的。宏实现如下:
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 }
而且,这有效。但是,当然,这根本不是我的目标。这是一个错误,还是我做错了?有解决方法吗?
答案 0 :(得分:4)
问题是宏实现中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
正如所料。
作为Miles Sabin 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]
}
特别注意applyImpl
中Signal
的第二个参数的更改。