簡體   English   中英

后代類中的宏擴展

[英]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;博士

  1. 目前,由於理論和實施原因,宏觀擴張期間的非本地運營可能很難或不可能穩健地執行。 SI-7046就是一個例子,這個問題提供了另一個例子。
  2. 有時,通過在宏觀中包含更大的范圍,可以將非本地宏觀擴張問題重新定位為本地宏觀擴張問題。 宏天堂插件提供的宏注釋可能有助於此目的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM