2013-05-08 151 views
6

我目前正在玩弄宏了一下,也許這是一個不好的想法,但這裏的PARAM是我的問題:斯卡拉宏分配解構功能

我有以下宏:

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的已棄用創建者時,需要SymbolTree,一切正常。如果我使用的是過時的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),並且實際上在未註釋的版本中出現編譯器錯誤。你可以發佈你用來調用宏的代碼嗎? – 2013-05-08 23:42:19

+0

更新了我的問題 – drexin 2013-05-09 00:08:56

+0

您一直在試圖使Scala中的析構函數?好想法! – 2014-01-21 00:36:58

回答

7

啊,現在我可以重現你的錯誤。任何時候你得到「這個條目似乎已經殺死了編譯器。」消息看行這樣的堆棧跟蹤:

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

你的下一步行動應該是開始於c.resetAllAttrs包裹的東西。說實話,我第一次無法重現錯誤的原因是因爲我在複製和粘貼代碼之後用c.resetAllAttrs(body)代替了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 2013-05-09 07:38:41