2013-05-08 2 views
6

Я в настоящее время играю немного с макросами и, возможно, что это плохая идея, так или иначе, но вот моя проблема:Scala макросов назначение параметров из деконструированного функции

У меня есть следующий макрос:

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

В случае Select я просто вызываю функцию и закрываю ресурс, отлично работает. Но в случае Function я хотел бы присвоить значение параметра ресурсу и вызвать тело. Когда я использую устаревшего создателя ValDef, который принимает Symbol и Tree, все работает нормально. Если я использую создателя с комментариями 4-args, я получаю ошибку компилятора, заявляя, что значение x$1 не входит в объем. Когда я смотрю на код, что оба варианта дают, он выглядит точно так же:

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

Есть ли может быть способ, чтобы просто использовать params.head и присвоить значение? Спасибо за любую помощь!

редактировать:

Я называю макрос так:

object Test extends App { 
    import Macros._ 

    class Foo { 
    def close() {} 

    def bar = 3 
    } 

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

Как я уже сказал, если я использую из-комментировал версию, он дает мне ошибку компилятора, который печатает AST и в конце этого сообщения: [error] symbol value x$1 does not exist in Test$delayedInit$body.apply

И я использую 2.10.1.

+1

Декомпозиция 'params.head' должна работать отлично: я не могу воспроизвести вашу ошибку (с 2.10.0 или 2.10.1) и на самом деле получить ошибку компилятора с раскомментированной версией. Можете ли вы опубликовать код, который вы используете, для вызова макроса? –

+0

обновил мой вопрос – drexin

+0

Вы пытались превратить деструкторов в Scala? Отличная идея! –

ответ

7

Ах, теперь я могу воспроизвести вашу ошибку. Каждый раз, когда вы получаете «Эта запись, похоже, убила компилятор». сообщение и посмотреть линии, как это в трассировке стека:

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

Ваш следующий шаг должен быть, чтобы начать обертывание материал в c.resetAllAttrs. Честно говоря, я не смог воспроизвести вашу ошибку в первый раз, потому что я заменил body в блоке c.resetAllAttrs(body) после копирования и вставки кода, даже не задумываясь об этом. На данный момент это просто рефлекс.

См. Например, this little single abstract method class demo для аналогичного экземпляра места, где этот трюк необходим.

В вашем случае, если мы имеем это:

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

Мы видим следующее, когда мы составляем тестовый код:

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

Точно по желанию.

+0

Работает как шарм, спасибо вам большое! – drexin

Смежные вопросы