使用 Scala 宏获取具体化函数的名称
Get Name of Reified Function Using Scala Macros
我想在遍历 Tree
的宏中对某些函数和其他变量引用进行模式匹配。目前,我正在基于 symbol.fullname
进行匹配,如下所示:
tree match {
case "$f($x)" if f.symbol != null
&& f.symbol.fullName == "_root_.mypackage.MyClass.myfunc" =>
...
case "$f($x)" if f.symbol != null
&& f.symbol.fullName == "_root_.mypackage.MyClass2.myfunc2" =>
...
case ...
}
但是,我想在编译这个宏的过程中额外检查这些函数是否确实存在,例如我不想使用 String,而是想直接使用引用,这样 IDE 可以告诉我 'myfunc' 名称中是否有拼写错误。我正在考虑使用 using reify
,但我不确定这是否有效。
现在假设我正在寻找 println
而不是 MyClass.myfunc
。我遇到的第一个问题是你不能 reify(println)
或任何其他函数直接引用,因为在 Scala 中没有函数引用,所以你必须写 reify(println _)
或更具体地在这种情况下 reify(println (_: String))
到 select 我们要调用什么 println 函数。使用以下代码,我收集了表达式中的所有符号,但遗憾的是只找到了 Predef(来自 Predef.println),但没有找到 println 本身:
println(reify( println (_:String) ).tree
.collect { case x if x.symbol != null => x.symbol.fullName } )
// List(<none>, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef)
在 scala(目前使用 2.12)中获取名称的任何想法?
reify(println)
编译。
你如何想象函数符号println (_:String)
? 这个函数是在哪里定义的?在 Predef
中定义了两个 方法 :
def println(): Unit
def println(x: Any): Unit
尝试
val mirror = scala.reflect.runtime.universe.runtimeMirror(this.getClass.getClassLoader) // at runtime
// val mirror = c.mirror // at compile time
mirror.staticClass("mypackage.MyClass").typeSignature.decl(TermName("myfunc"))
// method myfunc
或
typeOf[MyClass].decl(TermName("myfunc"))
// method myfunc
对于MyClass#myfunc
definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
// List(method println, method println)
两个println
definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
.filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
// List(method println)
对于println(Any):Unit
。
例如
def foo(x: Any): Unit = macro impl
def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
import c.universe._
val printlnSymb = definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
.filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
.head
x match {
case q"$f($x)" if f.symbol == printlnSymb =>
println("test")
}
q"()"
}
foo(println(1)) //Warning:scalac: test
reify( println (_:String) ).tree.collect { ...
只生成 List(<none>, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef)
因为树 reify( println (_:String) ).tree
没有类型检查(对于类型检查的树它生成 List($anonfun, x, java.lang.String, scala.Predef.println, scala.Predef.println, scala.Predef, x, java.lang.String)
)。
所以另一种选择是 c.typecheck
def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
import c.universe._
val printlnSymb = (c.typecheck(q"println(_:Any)", silent = false) match {
case q"($_) => $p($_)" => p
}).symbol
//val printlnSymb = (c.typecheck(reify { println(_:Any) }.tree, silent = false) match {
// case q"($_) => $p($_)" => p
//}).symbol
x match {
case q"$f($x)" if f.symbol == printlnSymb =>
println("test")
}
q"()"
}
为了得到一个具体化的表达式的符号,表达式树需要另一个显式地通过类型检查,所以下面的代码有效:
val mySym1 = c.typecheck(reify(mypackage.myfunction1 _).tree) match {
case q"{(..$_) => $f(..$_)}" => f.symbol
}
val mySym2 = c.typecheck(reify(mypackage.myfunction2 _).tree) match {
case q"{(..$_) => $f(..$_)}" => f.symbol
}
println(mySym.fullName)
// _root_.mypackage.myfunction1
然后可以根据符号匹配:
tree match {
case "$f($x)" if f.symbol != null => f.symbol match {
case x if x == mySym1 => ...
case x if x == mySym2 => ...
case ...
}
case ...
}
实际上有一个名为 def symbolOf[X]: TypeSymbol
的函数可用于从 Type 获取 TypeSymbol。但这只适用于 TypeSymbols,不适用于 TermSymbols,所以我们不能在函数上这样做。
但是,如果这是我们自己的函数,我们可以将 def myfunc(): Int = ...
中的定义替换为 object myfunc { def apply(): Int = ... }
。由于每个对象都有自己的类型(称为myfunc.type
),我们现在可以执行以下操作
package main
object myfunc { def apply(): Int = 1 }
object myfunc2 { def apply(): Int = 1 }
...
tree match {
case "$f.apply($x)" if f.symbol == symbolOf[myfunc.type] => ...
case "$f.apply($x)" if f.symbol == symbolOf[myfunc2.type] => ...
case ...
}
我想在遍历 Tree
的宏中对某些函数和其他变量引用进行模式匹配。目前,我正在基于 symbol.fullname
进行匹配,如下所示:
tree match {
case "$f($x)" if f.symbol != null
&& f.symbol.fullName == "_root_.mypackage.MyClass.myfunc" =>
...
case "$f($x)" if f.symbol != null
&& f.symbol.fullName == "_root_.mypackage.MyClass2.myfunc2" =>
...
case ...
}
但是,我想在编译这个宏的过程中额外检查这些函数是否确实存在,例如我不想使用 String,而是想直接使用引用,这样 IDE 可以告诉我 'myfunc' 名称中是否有拼写错误。我正在考虑使用 using reify
,但我不确定这是否有效。
现在假设我正在寻找 println
而不是 MyClass.myfunc
。我遇到的第一个问题是你不能 reify(println)
或任何其他函数直接引用,因为在 Scala 中没有函数引用,所以你必须写 reify(println _)
或更具体地在这种情况下 reify(println (_: String))
到 select 我们要调用什么 println 函数。使用以下代码,我收集了表达式中的所有符号,但遗憾的是只找到了 Predef(来自 Predef.println),但没有找到 println 本身:
println(reify( println (_:String) ).tree
.collect { case x if x.symbol != null => x.symbol.fullName } )
// List(<none>, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef)
在 scala(目前使用 2.12)中获取名称的任何想法?
reify(println)
编译。
你如何想象函数符号println (_:String)
? 这个函数是在哪里定义的?在 Predef
中定义了两个 方法 :
def println(): Unit
def println(x: Any): Unit
尝试
val mirror = scala.reflect.runtime.universe.runtimeMirror(this.getClass.getClassLoader) // at runtime
// val mirror = c.mirror // at compile time
mirror.staticClass("mypackage.MyClass").typeSignature.decl(TermName("myfunc"))
// method myfunc
或
typeOf[MyClass].decl(TermName("myfunc"))
// method myfunc
对于MyClass#myfunc
definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
// List(method println, method println)
两个println
definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
.filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
// List(method println)
对于println(Any):Unit
。
例如
def foo(x: Any): Unit = macro impl
def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
import c.universe._
val printlnSymb = definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
.filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
.head
x match {
case q"$f($x)" if f.symbol == printlnSymb =>
println("test")
}
q"()"
}
foo(println(1)) //Warning:scalac: test
reify( println (_:String) ).tree.collect { ...
只生成 List(<none>, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef)
因为树 reify( println (_:String) ).tree
没有类型检查(对于类型检查的树它生成 List($anonfun, x, java.lang.String, scala.Predef.println, scala.Predef.println, scala.Predef, x, java.lang.String)
)。
所以另一种选择是 c.typecheck
def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
import c.universe._
val printlnSymb = (c.typecheck(q"println(_:Any)", silent = false) match {
case q"($_) => $p($_)" => p
}).symbol
//val printlnSymb = (c.typecheck(reify { println(_:Any) }.tree, silent = false) match {
// case q"($_) => $p($_)" => p
//}).symbol
x match {
case q"$f($x)" if f.symbol == printlnSymb =>
println("test")
}
q"()"
}
为了得到一个具体化的表达式的符号,表达式树需要另一个显式地通过类型检查,所以下面的代码有效:
val mySym1 = c.typecheck(reify(mypackage.myfunction1 _).tree) match {
case q"{(..$_) => $f(..$_)}" => f.symbol
}
val mySym2 = c.typecheck(reify(mypackage.myfunction2 _).tree) match {
case q"{(..$_) => $f(..$_)}" => f.symbol
}
println(mySym.fullName)
// _root_.mypackage.myfunction1
然后可以根据符号匹配:
tree match {
case "$f($x)" if f.symbol != null => f.symbol match {
case x if x == mySym1 => ...
case x if x == mySym2 => ...
case ...
}
case ...
}
实际上有一个名为 def symbolOf[X]: TypeSymbol
的函数可用于从 Type 获取 TypeSymbol。但这只适用于 TypeSymbols,不适用于 TermSymbols,所以我们不能在函数上这样做。
但是,如果这是我们自己的函数,我们可以将 def myfunc(): Int = ...
中的定义替换为 object myfunc { def apply(): Int = ... }
。由于每个对象都有自己的类型(称为myfunc.type
),我们现在可以执行以下操作
package main
object myfunc { def apply(): Int = 1 }
object myfunc2 { def apply(): Int = 1 }
...
tree match {
case "$f.apply($x)" if f.symbol == symbolOf[myfunc.type] => ...
case "$f.apply($x)" if f.symbol == symbolOf[myfunc2.type] => ...
case ...
}