Prima di tutto, il caso 1 + "foo"
sta per essere difficile perché non c'è in realtà alcuna conversione implicita succedendo lì: Int
stesso really, truly does have this +
method (unfortunately).
Quindi sei fuori di fortuna se questo è il tuo caso d'uso, ma è possibile fare quello che stai descrivendo in generale. Si assume la seguente configurazione nei miei esempi qui sotto:
case class Foo(i: Int)
case class Bar(s: String)
implicit def foo2bar(foo: Foo) = Bar(foo.i.toString)
Prima per l'elegante approccio:
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
)
}
}
che funziona come desiderato:
scala> ConversionDetector.sniff[Bar](Foo(42))
res1: Boolean = true
scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42)))
res2: Boolean = false
Purtroppo questo richiede macro non tipizzati, che sono attualmente disponibili solo in Macro Paradise.
È possibile ottenere quello che vuoi con semplici vecchie def
macro in 2.10, ma è un po 'di 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
}
)
}
}
E ancora:
scala> ConversionDetector.sniff[Bar](Foo(42))
res1: Boolean = true
scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42)))
res2: Boolean = false
Il trucco è quello di cercare posti dove vediamo l'applicazione della funzione nel nostro albero di sintassi astratto e quindi per verificare se le posizioni del nodo Apply
e del suo fun
figlio hanno la stessa colonna, il che indica che la chiamata al metodo non è esplicitamente presente nell'origine.
grazie per la risposta. Non sapevo che 'Int' ha un metodo' + 'che accetta' String' ma intendevo comunque il caso generale. Stavo pensando di usare anche 'Position's, ma questo in effetti ha un odore un po '" hacky ". Penso che sarebbe bello se 'Tree's avesse qualche bandiera che dicesse" questo albero è stato automaticamente inferito dal compilatore ". – ghik
Ho appena trovato un [commento] (https://github.com/scala/scala/blob/master/src/reflect/scala/reflect/internal/Trees.scala#L431) nelle fonti 'scala-reflect' che parla circa un potenziale flag su AST 'Apply' che indicherà la conversione implicita. E sembra che ora ci sia una classe separata per indicare questo (è interno, purtroppo). – ghik
Sfortunatamente usare il trucco 'Position' significa che il tuo codice esplode quando usato nel REPL. @ghik, penso che tu sia sulla strada giusta con il tuo ultimo commento, che la risposta di @ EugeneBurmako elabora. –