2013-05-07 16 views
8

Jak uzyskać makro scala, aby zastąpić wywołanie metody?Jak uzyskać makro scala, aby zastąpić wywołanie metody

Moim celem jest stworzenie cechy o nazwie ToStringAdder. Przypuśćmy, że mam obiekt x z tą cechą, a kiedy zadzwonię pod numer x.add(any), chcę, aby makro faktycznie wywoływało x.add(any, string), gdzie ciąg jest ciągiem reprezentującym AST. (Jest tak, że mogę mieć ładne tostring, gdy "dowolne" jest funkcją).

Z wyjątkiem Expektuj wszystkie te przykłady, które widziałem, efektywnie używają wywołań metod statycznych: obiekt, na który wywoływane jest makro, nie jest używany. Expecty ma następującą metodę, która daje mi wskazówkę, jak wykryć "ukryty to", ale nie mogłem znaleźć sposobu, aby odwołać się do niego w reify połączenia.

private[this] def recordAllValues(expr: Tree): Tree = expr match { 
    case New(_) => expr // only record after ctor call 
    case Literal(_) => expr // don't record 
    // don't record value of implicit "this" added by compiler; couldn't find a better way to detect implicit "this" than via point 
    case Select([email protected](_), y) if getPosition(expr).point == getPosition(x).point => expr 
    case _ => recordValue(recordSubValues(expr), expr) 
    } 

Jak mogę zastąpić wywołanie obiektu, do którego zostało wywołane makro. Kod mam w tej chwili jest poniżej, a to jest kod w zaproszeniu zreifikować że musi być sortowane

trait ToStringAdder { 
    def add(param: Any): Any = macro ToStringAdder.toStringAndValueImpl 
    def add(param: Any, toStringBasedOnAST: String): Any ; //This is the actual method I want the above method call to be replaced by 
} 

object ToStringAdder { 
    def toStringAndValueImpl(c: Context)(param: c.Expr[Any]): c.Expr[Unit] = { 
    import c.universe._ 
    val paramRep = show(param.tree) 
    val paramRepTree = Literal(Constant(paramRep)) 
    val paramRepExpr = c.Expr[String](paramRepTree) 
    //need to put something here 
    reify { c.someMethodCall("something to represent the method any", param.splice, paramRepExpr.splice) } 
    } 
} 
+0

To jest naprawdę ładny pytanie jeśli rozumiem go poprawnie. Sądzę, że chodzi o to, że przy użyciu toString na "funkcji", która zwraca unikalny AST, można zaimplementować operatora równości w 'Function's. Można wtedy robić niesamowite rzeczy, na przykład nie martwić się powielaniem funkcji w "liście" - wystarczy wywołać 'toSet'. – samthebest

Odpowiedz

4

Można dostać Tree dla instancji ToStringAdder jako c.prefix.

Spróbuj tego:

reify { c.Expr[ToStringAdder](c.prefix.tree).splice.add(param.splice, c.literal(paramRep).splice) } 

Dowód to działa:

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

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

trait ToStringAdder { 
    def add(param: Any): Any = macro ToStringAdder.toStringAndValueImpl 
    def add(param: Any, toStringBasedOnAST: String): Any ; //This is the actual method I want the above method call to be replaced by 
} 

object ToStringAdder { 
    def toStringAndValueImpl(c: Context)(param: c.Expr[Any]): c.Expr[Any] = { 
    import c.universe._ 
    val paramRep = show(param.tree) 
    reify { (c.Expr[ToStringAdder](c.prefix.tree)).splice.add(param.splice, c.literal(paramRep).splice) } 
    } 
} 


// Exiting paste mode, now interpreting. 

import scala.language.experimental.macros 
import reflect.macros.Context 
defined trait ToStringAdder 
defined module ToStringAdder 

scala> class ToStringAdder1 extends ToStringAdder { 
    | def add(param: Any, toStringBasedOnAST: String): Any = s"param: $param \ntoStringBasedOnAST: $toStringBasedOnAST" 
    | } 
defined class ToStringAdder1 

scala> new ToStringAdder1().add((i: Int) => i*2) 
res0: Any = 
param: <function1> 
toStringBasedOnAST: ((i: Int) => i.*(2)) 
+0

Dzięki za szybką odpowiedź. Otrzymuję komunikat "wartość dodana nie jest członkiem org.autotdd.scalamacros.ToStringAdder". A wgląd w kod kontroli/spacji nie pokazuje mi żadnych opcji do dodania. Czy muszę wpisać cast lub coś takiego? –

+0

@StaveEscura: Dodałem przykład kodu do mojej odpowiedzi. Wystąpił błąd w twoim kodzie: 'c.Expr [Unit]' zamiast 'c.Expr [Any]' jako typ wyniku 'toStringAndValueImpl'. – senia

+0

Po prostu zadziałało. Naprawdę doceniam pomoc! –

Powiązane problemy