让我们假设将这两个等效表达式传递给Scala宏:
1+"foo"
any2stringadd(1)+"foo"
有没有办法在宏内区分这两个?
答案 0 :(得分:3)
首先,1 + "foo"
案例会变得棘手,因为实际上并没有发生任何隐式转换:Int
本身really, truly does have this +
method(unfortunately)。
如果那是你的使用案例,那么你运气不好,但你可以做更普遍的描述。我将在下面的示例中假设以下设置:
case class Foo(i: Int)
case class Bar(s: String)
implicit def foo2bar(foo: Foo) = Bar(foo.i.toString)
首先是优雅的方法:
object ConversionDetector {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def sniff[A](tree: _): Boolean = macro sniff_impl[A]
def sniff_impl[A: c.WeakTypeTag](c: Context)(tree: c.Tree) = {
// First we confirm that the code typechecks at all:
c.typeCheck(tree, c.universe.weakTypeOf[A])
// Now we try it without views:
c.literal(
c.typeCheck(tree, c.universe.weakTypeOf[A], true, true, false).isEmpty
)
}
}
根据需要起作用:
scala> ConversionDetector.sniff[Bar](Foo(42))
res1: Boolean = true
scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42)))
res2: Boolean = false
不幸的是,这需要非类型化的宏,目前只能在Macro Paradise中使用。
你可以在2.10中使用普通的旧def
宏获得你想要的东西,但它有点像黑客:
object ConversionDetector {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def sniff[A](a: A) = macro sniff_impl[A]
def sniff_impl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]) = {
import c.universe._
c.literal(
a.tree.exists {
case app @ Apply(fun, _) => app.pos.column == fun.pos.column
case _ => false
}
)
}
}
再次:
scala> ConversionDetector.sniff[Bar](Foo(42))
res1: Boolean = true
scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42)))
res2: Boolean = false
诀窍是在我们的抽象语法树中查找我们看到函数应用程序的位置,然后检查Apply
节点及其fun
子节点的位置是否具有相同的列,表示方法调用未明确存在于源中。
答案 1 :(得分:2)
这是一个黑客,但它可能会帮助你:
import scala.reflect.macros.Context
import language.experimental.macros
object Macros {
def impl(c: Context)(x: c.Expr[Int]) = {
import c.universe._
val hasInferredImplicitArgs = x.tree.isInstanceOf[scala.reflect.internal.Trees#ApplyToImplicitArgs]
val isAnImplicitConversion = x.tree.isInstanceOf[scala.reflect.internal.Trees#ApplyImplicitView]
println(s"x = ${x.tree}, args = $hasInferredImplicitArgs, view = $isAnImplicitConversion")
c.literalUnit
}
def foo(x: Int) = macro impl
}
import language.implicitConversions
import scala.reflect.ClassTag
object Test extends App {
def bar[T: ClassTag](x: T) = x
implicit def foo(x: String): Int = augmentString(x).toInt
Macros.foo(2)
Macros.foo(bar(2))
Macros.foo("2")
}
08:30 ~/Projects/210x/sandbox (2.10.x)$ ss
x = 2, args = false, view = false
x = Test.this.bar[Int](2)(ClassTag.Int), args = true, view = false
x = Test.this.foo("2"), args = false, view = true