如何区分编译器推断的隐式转换和显式调用的隐式转换? [英] How to distinguish compiler-inferred implicit conversion from explicitly invoked one?
问题描述
让我们想象一下将这两个等效表达式传递给Scala宏:
Let's imagine passing these two equivalent expressions to a Scala macro:
- 具有编译器推断的隐式转换:
1+"foo"
- 具有显式调用的隐式转换:
any2stringadd(1)+"foo"
- with compiler-inferred implicit conversion:
1+"foo"
- with explicitly invoked implicit conversion:
any2stringadd(1)+"foo"
在宏中是否可以区分这两个?
Is there a way to distinguish between these two inside the macro?
推荐答案
首先,1 + "foo"
情况将非常棘手,因为实际上并没有发生任何隐式转换:Int
本身确实,确实有此+
方法(不幸的是).
First of all, the 1 + "foo"
case is going to be tricky because there isn't actually any implicit conversion happening there: Int
itself really, truly does have this +
method (unfortunately).
如果这是您的用例,那么您就不走运了,但是有可能做更一般的描述.我将在下面的示例中假设以下设置:
So you're out of luck if that's your use case, but it is possible to do what you're describing more generally. I'll assume the following setup in my examples below:
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 .
Unfortunately this requires untyped macros, which are currently only available in Macro Paradise.
在2.10中,您可以使用普通的旧def
宏来获得所需的内容,但这有点麻烦:
You can get what you want with plain old def
macros in 2.10, but it's a bit of a hack:
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
子节点的位置是否在同一列中,这表明该方法调用未明确显示在源中.
The trick is to look for places where we see function application in our abstract syntax tree, and then to check whether the positions of the Apply
node and its fun
child have the same column, which indicates that the method call isn't explicitly present in the source.
这篇关于如何区分编译器推断的隐式转换和显式调用的隐式转换?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!