[英]Macro expansion in descendant classes
我有下一個類結構:
trait SomeType
trait Root {
val allMySomeTypes: Seq[SomeType]
}
class Child extends Root {
object MyType1 extends SomeType {...}
object MyType2 extends SomeType {...}
}
我想初始化val allMySomeTypes作為擴展在具體類中定義的SomeType的所有對象的Seq。 所以對於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類中有空序列。 我可以在沒有在Child類中額外輸入而不使用運行時反射的情況下構建實際成員的Seq嗎?
子類檢測的一般情況
有趣的是,幾天前我們在scala-user上進行了類似的討論 ,在那里我們討論了在一般情況下列出給定類的所有子類的可能性。 這是我當時發布的答案:
目前不可能枚舉任意類的所有子類,但我們確實有一個名為ClassSymbol.knownDirectSubclasses
的API,它枚舉了密封的后代。
但不幸的是,即便這樣也不容易。 knownDirectSubclasses API在人們以某種方式使用它的意義上或多或少都很好,但它也有一個嚴重的缺陷,我們目前不知道如何修復: https : //groups.google.com/forum/#! topic / scala-internals / LRfKffsPxVA 。
子類檢測的特殊情況
但是,這個StackOverflow問題會詢問更具體的內容。 我們如何將自己局限於某個類的成員,然后可以選擇那些我們感興趣的類的子類嗎?
好吧,事實證明,盡管目前有一個API來處理它( c.enclosingClass
系列的c.enclosingTree
),即使這個特殊情況仍然無法穩健地處理。
問題是Scala宏在類型檢查期間被擴展,這意味着在給定宏被擴展時,它的封閉樹被強調了。 “被類型檢查”狀態意味着一些封閉的樹可能暫時處於不一致的狀態,如果試圖檢查它們就會崩潰。 在Scala Macro Annotations上有一個更詳細的討論:帶注釋類型的c.TypeCheck會導致StackOverflowError ,但在這里我只舉一個簡單的例子。
例如,如果您的宏在具有未指定返回類型的方法內擴展(例如def foo = yourMacro(1, 2, 3)
),那么調用c.enclosingDef.symbol.typeSignature
將失敗宏擴展,因為,以及,要知道方法的簽名,你需要推斷它的返回類型,但要推斷你需要擴展宏的返回類型,你需要知道方法的簽名等。順便說一下,編譯器為了不在這里無限循環,它足夠聰明 - 它會提前打破循環並顯示循環引用錯誤。
這意味着要很好地處理非局部宏擴展,我們需要對類型簽名及其依賴關系進行某種聲明性抽象,而不僅僅是命令式typeSignature
方法。 在過去的幾個月里,我一直在思考這個問題,但到目前為止還沒有得到任何令人滿意的結果,這也是為什么在Scala 2.11中我們要棄用非本地c.enclosingTree
的原因之一c.enclosingTree
方法: https : //github.com/scala/scala/pull/3354 。
隨着宏觀注釋的出現,這個主題將變得更加重要,所以我們還沒有承認失敗。 我認為,期待這一領域取得進展是合理的,但我們沒有具體的日期可以提供給這一領域。
一種可能的解決方法
正如我上面提到的,我們在檢測子類的任務中遇到的“唯一”問題是它是一個非執行它的宏擴展操作。 那么我們如何將其轉變為本地運營呢? 事實證明這是可能的。
通過在Child
類上放置宏注釋,在宏中,您將能夠在類型檢查之前看到類的所有成員,這意味着檢查這些成員不會導致潛在的虛假循環錯誤。
但是,如果這些成員沒有受到嚴格監控,那么他們對我們有什么好處呢? 我們需要類型來執行子類檢查,對吧? 嗯,是的,不。 我們確實需要類型,但我們不必從成員本身獲取這些類型。 我們可以使用帶注釋的類,復制它,對其進行檢查,然后檢查類型檢查的結果,而不必擔心會破壞被注意者。 這是一個為實施此策略提供靈感的示例: 在處理宏注釋時無法訪問Parent的成員 。
TL;博士
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.