2013-03-19 1 views

Antwort

3

Vor allem die 1 + "foo" Fall wird schwierig sein, weil es nicht wirklich jede implizite Konvertierung dort geschieht, ist: Int selbst really, truly does have this + method (unfortunately).

Sie haben also kein Glück, wenn das Ihr Anwendungsfall ist, aber es ist möglich, das zu tun, was Sie allgemein beschreiben.

case class Foo(i: Int) 
case class Bar(s: String) 
implicit def foo2bar(foo: Foo) = Bar(foo.i.toString) 

Zuerst für den eleganten Ansatz:: Ich werde die folgende Einstellung in meinen Beispielen unten nehmen

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 
    ) 
    } 
} 

die wie gewünscht funktioniert:

scala> ConversionDetector.sniff[Bar](Foo(42)) 
res1: Boolean = true 

scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42))) 
res2: Boolean = false 

Leider ist dieses nicht typisierten Makros erfordert, welche derzeit nur in Macro Paradise verfügbar sind.

Sie bekommen, was Sie in 2.10 mit einfachen alten def Makros wollen, aber es ist ein bisschen wie ein 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 
     } 
    ) 
    } 
} 

Und wieder:

scala> ConversionDetector.sniff[Bar](Foo(42)) 
res1: Boolean = true 

scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42))) 
res2: Boolean = false 

Der Trick ist, um die Plätze zu suchen Wo wir in unserem abstrakten Syntaxbaum Funktionsanwendungen sehen und dann prüfen, ob die Positionen des Apply-Knotens und seines untergeordneten Elements fun dieselbe Spalte haben, was darauf hinweist, dass der Methodenaufruf nicht explizit in der Quelle vorhanden ist.

+0

Dank für die Beantwortung. Ich wusste nicht, dass "Int" eine Methode '+' hat, die 'String' akzeptiert, aber ich meinte den allgemeinen Fall trotzdem. Ich habe darüber nachgedacht, 'Position's auch zu benutzen, aber das riecht tatsächlich ein bisschen 'hacky'. Ich denke, es wäre cool, wenn 'Tree's eine Flagge hätte, die sagen würde" dieser Baum wurde automatisch vom Compiler abgeleitet ". – ghik

+1

Ich habe gerade einen [Kommentar] (https://github.com/scala/scala/blob/master/src/reflect/scala/reflect/internal/Trees.scala#L431) in 'scala-reflect' Quellen gefunden, die sprechen über ein potentielles Flag auf 'Apply' AST, das eine implizite Konvertierung anzeigt. Und es sieht jetzt so aus, dass es eine separate Klasse gibt, um dies anzuzeigen (leider ist es intern). – ghik

+0

Leider führt die Verwendung des 'Position'-Tricks dazu, dass Ihr Code bei der Verwendung in der REPL explodiert. @ghik, ich denke du bist auf dem richtigen Weg mit deinem letzten Kommentar, auf den @ EugeneBurmakos Antwort näher eingeht. –

2

Das ist ein Hack, aber es könnte Ihnen helfen:

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 
Verwandte Themen