2014-01-16 40 views
3

我隔壁班的結構:宏擴展在派生類

trait SomeType 

trait Root { 
    val allMySomeTypes: Seq[SomeType] 
} 

class Child extends Root { 
    object MyType1 extends SomeType {...} 
    object MyType2 extends SomeType {...} 
} 

我想初始化VAL allMySomeTypes如延長混凝土類中定義SOMETYPE所有對象的序列。因此對於Child實例,它將是val allMySomeTypes:Seq [SomeType] = Seq(MyType1,MyType2)。

我寫了一個宏尋找一些基本類型的對象:

def getMembersOfCurrentByType_impl[S](c: Context)(implicit ev: c.WeakTypeTag[S]) = { 
    import c.universe._ 
    val seqApply = Select(reify(Seq).tree, newTermName("apply")) 
    val objs = c.enclosingClass.symbol.typeSignature.declarations.collect { 
    case o: ModuleSymbol if o.typeSignature <:< ev.tpe => Ident(o) //Select(c.prefix.tree, o) 
    }.toList 
    c.Expr[Seq[S]] {Apply(seqApply, objs)} 
} 

,並綁定到

trait Root { 
    val allMySomeTypes: Seq[SomeType] = macro getMembersOfCurrentByType_impl[SomeType] 
} 

但是,很明顯,我有空序列在兒童類,因爲宏展開基礎特徵。 我可以在沒有在Child類中額外鍵入而不使用運行時反射的情況下構建實際成員的Seq嗎?

回答

5

子檢測

一般情況下,有趣的是,我們有a similar discussion at scala-user短短數天前,我們交談有關列出一個給定類的子類,一般情況下的可能性。以下是我當時發佈的答案:

目前無法枚舉任意類的所有子類,但我們確實有一個名爲ClassSymbol.knownDirectSubclasses的API,它枚舉了密封的後代。

但不幸的是,即使這並不容易。已知的DirectSubclasses API在某種程度上使用它的意義上或多或少是好的,但它也有一個嚴重的缺陷,我們目前不知道如何解決:https://groups.google.com/forum/#!topic/scala-internals/LRfKffsPxVA

子檢測

的具體情況然而這個StackOverflow的問題詢問更具體的東西。如果我們僅限於某個類的成員,那麼是否有可能挑選出我們感興趣的類的子類?

嗯,事實證明,儘管有目前對付,一個API(家庭c.enclosingTree方法c.enclosingClass),即使這種特殊情況下還不能有力尚未處理。

事情是Scala宏在類型檢查期間被擴展,這意味着在給定的宏被擴展時,它的封閉樹正在被類型檢查。 「被類型化查詢」狀態意味着一些封閉的樹可能會暫時處於不一致的狀態,如果試圖檢查它們就會崩潰。在Scala Macro Annotations: c.TypeCheck of annotated type causes StackOverflowError有一個更詳細的討論,但我在這裏舉一個簡單的例子。例如,如果您的宏在具有未指定返回類型的方法(例如def foo = yourMacro(1, 2, 3))內展開,則調用c.enclosingDef.symbol.typeSignature將導致宏展開失敗,因爲知道該方法的簽名,您需要推斷它的返回類型,但要推斷出你需要擴展宏的返回類型,爲此你需要知道方法的簽名等等。順便說一句,編譯器足夠聰明以便在這裏無限循環 - 它將盡早打破循環並顯示循環引用錯誤。

這意味着要很好地處理非本地宏擴展,我們需要對類型簽名和它們的依賴關係進行某種類型的聲明性抽象,而不僅僅是一種強制性的方法typeSignature。過去幾個月來我一直在思考這個問題,但到目前爲止還沒有出現任何令人滿意的結果,這就是爲什麼在Scala 2.11中我們會拋棄非本地方法c.enclosingTree方法的原因之一:https://github.com/scala/scala/pull/3354

隨着宏註釋的出現,這個主題將變得更加重要,所以我們不會承認失敗。我認爲,期待在這方面取得進展是合理的,但是我們沒有具體的日期可以提供給我們。

可能的解決方法

正如我上面所提到的,「唯一」的問題,我們有具有檢測小類中所述的任務是,它即非本地相對於執行所述宏擴展的操作它。那麼我們如何將其轉變爲本地運營?事實證明,這是可能的。

通過在Child類上放置宏註釋,在您的宏中,您可以在類型檢查前看到類的所有成員,這意味着檢查這些成員不會導致潛在的虛假循環錯誤。

但是,如果這些成員沒有被類型檢查,那麼他們對我們有什麼好處?我們需要類型來執行子類檢查,對吧?那麼,是的,不。我們的確需要類型,但我們不必從成員本身獲取這些類型。我們可以帶註釋的類,複製它,檢查它,然後檢查類型檢查的結果,而不用擔心擾亂註冊者。以下示例將爲實施此策略提供靈感:Can't access Parent's Members while dealing with Macro Annotations

TL;博士

  1. 此刻,宏膨脹期間非本地操作可以是硬的或不可能的魯棒執行的,因爲理論和實施過程中的原因,。 SI-7046就是一個例子,這個問題提供了另一個例子。
  2. 有時可以通過在宏中包含更大的範圍來將非局部宏觀擴展問題重新定義爲局部宏觀擴展問題。宏觀天堂插件提供的宏註釋可能對此有所幫助。
+0

謝謝你這麼完整和解釋的答案!我將仔細研究宏註釋。 – newf