2012-12-07 12 views
8

Czy istnieje wygodny sposób przekształcania obiektu MethodSymbol w lewą stronę drzewa definicji metody (to jest, DefDef) w Scali 2.10?Tworzenie drzewa definicji metody z symbolu metody i obiektu

Załóżmy na przykład, że chcę utworzyć makro, które zajmie instancję cechy i zawinie wszystkie te cechy, korzystając z niektórych funkcji debugowania. Mogę napisać następujące:

import scala.language.experimental.macros 
import scala.reflect.macros.Context 

object WrapperExample { 
    def wrap[A](a: A): A = macro wrap_impl[A] 

    def wrap_impl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]) = { 
    import c.universe._ 

    val wrapped = weakTypeOf[A] 
    val f = Select(reify(Predef).tree, "println") 

    val methods = wrapped.declarations.collect { 
     case m: MethodSymbol if !m.isConstructor => DefDef(
     Modifiers(Flag.OVERRIDE), 
     m.name, 
     Nil, Nil, 
     TypeTree(), 
     Block(
      Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil), 
      Select(a.tree, m.name) 
     ) 
    ) 
    }.toList 

    //... 
} 

Mam pomijana nudny biznes od przyklejania tych metod w nowej klasie anonimowej implementującej cechę, a następnie instancji klasy, które można znaleźć kompletny pracy przykład here jeśli was” jestem zainteresowany.

Teraz mogę napisać to na przykład:

scala> trait X { def foo = 1; def bar = 'a } 
defined trait X 

scala> val x = new X {} 
x: X = [email protected] 

scala> val w: X = WrapperExample.wrap[X](x) 
w: X = [email protected] 

scala> w.foo 
Calling: foo 
res0: Int = 1 

scala> w.bar 
Calling: bar 
res1: Symbol = 'a 

Tak to działa, ale tylko w bardzo prostych przypadkach, to nie będzie, jeśli cecha ma metod z listami parametrów, z modyfikatorów dostępu, adnotacje, itd.

To, czego naprawdę chcę, to funkcja, która pobiera symbol metody i drzewo dla nowego obiektu i zwraca wartość DefDef. Zacząłem pisać jedną ręką, ale to wymaga dużo fiddly rzeczy tak:

List(if (method.isImplicit) Some(Flag.IMPLICIT) else None, ...) 

Która jest denerwujące, gadatliwe i podatne na błędy. Czy brakuje mi lepszego sposobu na to w nowym interfejsie API Reflection?

Odpowiedz

4

Zgodnie z moją wiedzą, nie ma standardowego sposobu przejścia z symbolu do drzewa definiującego.

Najlepszym rozwiązaniem byłoby prawdopodobnie powtórzenie przez c.enclosingRun.units, powtarzając w każdym z drzew unit.body. Jeśli zobaczysz DefDef, który ma symbol równy twojemu symbolowi, to osiągniesz cel. upd. Nie zapomnij o duplicate definiowaniu drzewa przed ponownym użyciem!

Ta technika jest daleka od bycia najwygodniejszą rzeczą na świecie, ale powinna działać.

+0

Dzięki (i +1), ale co, jeśli cecha, którą chcę opakować, pochodzi z biblioteki? W takim razie utknąłem z podejściem powyżej, prawda? –

+1

Ah Widzę, co masz na myśli. Możesz użyć tego interfejsu API: https://github.com/scalamacros/kepler/blob/0acb8a30c379f268e8a3e1340504530493a1a1dc/src/reflect/scala/reflect/api/Trees.scala#L2480. Usunęliśmy go w wersji 2.10.1, ale możesz sprawdzić, jak jest zaimplementowany: https://github.com/scalamacros/kepler/blob/0acb8a30c379f268e8a3e1340504530493a1a1dc/src/reflect/scala/reflect/internal/Trees.scala#L975 . –

2

Możesz wypróbować następujące rzeczy. Działa to z wieloma parametrami i funkcjami curry oraz parametrami typu;)

val methods = wrapped.declarations.collect { 
    case m: MethodSymbol if !m.isConstructor => DefDef(
    Modifiers(Flag.OVERRIDE), 
    m.name, 
    m.typeParams.map(TypeDef(_)), 
    m.paramss.map(_.map(ValDef(_))), 
    TypeTree(m.returnType), 
    Block(
     Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil), 
     m.paramss.foldLeft(Select(a.tree.duplicate, m.name): Tree)((prev, params) => 
     Apply(prev, params.map(p => Ident(p.name))) 
    ) 
    ) 
) 
}.toList 
+0

Istnieje również 'DefDef (sym, tree)', który robi to samo, ale jak to zostało wspomniane poniżej, ta metoda została właśnie przestarzała wraz z 'TypeDef (sym)' i 'ValDef (sym)'. upd. Nie robi tego samego, ponieważ twój kod wydaje się omijać istniejące modyfikatory. –

+0

Zobacz także moje komentarze do istoty PO: https://gist.github.com/4234441. Twój kod ma taki sam subtelny problem jak ten kod. –

+0

Dzięki Eugene. Odpowiednio zaktualizowałem swoją odpowiedź. Może to wyjaśnić niektóre problemy, które mam na niektóre z moich makr z nieprzyjemnymi komplikacjami. – Leo

Powiązane problemy