2013-05-08 6 views
6

Attualmente sto giocando un po 'con le macro e forse questa è una cattiva idea comunque, ma ecco il mio problema:macro Scala assegnano param della funzione decostruita

Ho la seguente macro:

def using[A <: { def close(): Unit }, B](resource: A)(f: A => B) = macro usingImpl[A, B] 

def usingImpl[A <: { def close(): Unit }, B](c: Context)(resource: c.Expr[A])(f: c.Expr[A => B]): c.Expr[B] = { 

    import c.universe._ 


    f.tree match { 
    case Function(params, body) => 
     //val ValDef(modifiers, name, tpt, _) = params.head 
     c.Expr[B](
     Block(
      List(
      //ValDef(modifiers, name, tpt, resource.tree) 
      ValDef(params.head.symbol, resource.tree) 
     ), 
      body 
     ) 
    ) 

    case _: Select => 
     reify { 
     val res = resource.splice 
     try { 
      f.splice(res) 
     } finally { 
      res.close() 
     } 
     } 
    } 
} 

In caso di Select, richiama semplicemente la funzione e chiudo la risorsa, funziona correttamente. Ma nel caso di un Function, vorrei assegnare il valore param alla risorsa e chiamare il corpo. Quando utilizzo il creatore deprecato di ValDef, che accetta uno Symbol e uno Tree, tutto funziona correttamente. Se sto usando il creatore di 4 argomenti, ho ricevuto un errore del compilatore, affermando che il valore x$1 non rientra nell'ambito. Quando guardo il codice che entrambe le versioni producono, sembra esattamente lo stesso:

Expr[Int]({ 
    <synthetic> val x$1: Test.Foo = new Test.this.Foo(); 
    x$1.bar.+(23) 
}) 

C'è forse un modo, usare semplicemente params.head e assegnare un valore? Grazie per qualsiasi aiuto!

modificare:

io chiamo la macro in questo modo:

object Test extends App { 
    import Macros._ 

    class Foo { 
    def close() {} 

    def bar = 3 
    } 

    println(using(new Foo)(_.bar + 3)) 
} 

Come ho detto, se sto utilizzando la versione non ha commentato, mi dà un errore del compilatore, che le stampe l'AST e alla fine questo messaggio: [error] symbol value x$1 does not exist in Test$delayedInit$body.apply

E sto usando 2.10.1.

+1

La decostruzione di 'params.head' dovrebbe funzionare perfettamente - non riesco a riprodurre il tuo errore (con 2.10.0 o 2.10.1), e in effetti ottengo un errore del compilatore con la versione non commentata. Puoi pubblicare il codice che stai utilizzando per chiamare la macro? –

+0

aggiornato la mia domanda – drexin

+0

Hai provato a creare distruttori in Scala? Grande idea! –

risposta

7

Ah, ora posso riprodurre il tuo errore. Ogni volta che ottieni "Questa voce sembra aver ucciso il compilatore". messaggi e vedere linee come questo nella traccia dello stack:

at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49) 

tua prossima mossa dovrebbe essere quella di iniziare avvolgendo roba in c.resetAllAttrs. Ad essere sinceri, la ragione per cui non sono riuscito a riprodurre il tuo errore la prima volta è perché avevo sostituito body nel blocco con c.resetAllAttrs(body) dopo aver copiato e incollato il codice, senza nemmeno pensarci. È solo un riflesso a questo punto.

Vedere ad esempio this little single abstract method class demo per un'istanza simile di un luogo in cui è necessario questo trucco.

Nel tuo caso, se abbiamo questa:

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

object Macros { 
    def using[A <: { def close(): Unit }, B](resource: A)(f: A => B) = 
    macro usingImpl[A, B] 

    def usingImpl[A <: { def close(): Unit }, B](c: Context) 
    (resource: c.Expr[A])(f: c.Expr[A => B]): c.Expr[B] = { 
    import c.universe._ 

    val expr = f.tree match { 
     case Function(ValDef(modifiers, name, tpt, _) :: Nil, body) => 
     c.Expr[B](
      Block(
      ValDef(modifiers, name, tpt, resource.tree) :: Nil, 
      c.resetAllAttrs(body) 
     ) 
     ) 

     case _: Select => reify { 
     val res = resource.splice 
     try { f.splice(res) } finally { res.close() } 
     } 
    } 

    println(expr) 
    expr 
    } 
} 

Vedremo quanto segue quando compiliamo il codice di prova:

Expr[B]({ 
    <synthetic> val x$1: $line3.$read.$iw.$iw.Test.Foo = new Test.this.Foo(); 
    x$1.bar.$plus(3) 
}) 

Esattamente come desiderato.

+0

Funziona come un incantesimo, grazie mille! – drexin