我目前正在使用表示樹的型別層次結構和表示該樹中根到節點路徑步驟的伴隨型別層次結構。有不同種類的節點,因此可以在每個節點上采取不同的步驟。因此,節點型別有一個型別成員,該成員設定為包括所有有效步驟的特征。舉個例子:
// Steps
sealed trait Step
sealed trait LeafStep extends Step
sealed trait HorizontalStep extends Step
sealed trait VerticalStep extends Step
object Left extends HorizontalStep
object Right extends HorizontalStep
object Up extends VerticalStep
object Down extends VerticalStep
// The Tree
sealed trait Tree {
type PathType <: Step
}
case class Leaf() extends Tree {
override type PathType = LeafStep
}
case class Horizontal(left: Tree, right: Tree) extends Tree {
override type PathType = HorizontalStep
}
case class Vertical(up: Tree, down: Tree) extends Tree {
override type PathType = VerticalStep
}
在這個例子中,給定一棵樹,路徑Seq(Up, Right)會告訴我們去到根的“上”孩子的“右”孩子(假設樹的節點有合適的型別)。當然,導航樹涉及大量使用PartialFunctions 的代碼,示例中沒有顯示。但是,在該程序中,我想提供一個型別安全的回呼,該回呼會針對所采取的每個步驟進行通知,并且它的引數包括該步驟和相應的樹節點。
我目前的做法是一個函式def callback(from: Tree, to: Tree)(step: from.PathType)。這在呼叫者端沒有問題,但是在實際實作這樣一個與傳遞給它的資料一起作業的回呼時遇到了一個問題。
def callback(from: Tree, to: Tree)(step: from.PathType) = {
from match {
case f: Horizontal => step match {
case Left => ??? // do useful stuff here
case Right => ??? // do other useful stuff here
}
case _ => ()
}
}
在函式中,編譯器不相信Left和Right是 型別from.PathType。當然,我可以簡單地添加.asInstanceOf[f.PathType],代碼似乎可以使用它。但是,外部匹配給我們的f是 type Horizontal。我們知道物件的 是PathType并且因為是相同的我們也知道是相同的型別。最后,我們可以檢查兩者并擴展。因此,即使沒有強制轉換,上面的代碼也應該始終是型別安全的。HorizontalHorizontalStepffromfrom.PathTypeLeftRightHorizontalStep
這是Scala編譯器沒有檢查的推理,還是我錯過了型別可能不同的情況?有沒有更好的方法來實作我的型別安全回呼目標?我正在使用 Scala 2.12.15
uj5u.com熱心網友回復:
恐怕我對為什么不進行型別檢查沒有徹底的解釋。(似乎對于這種依賴型別的引數,編譯器只是無法將從模式匹配中獲得的知識應用到另一個引數(沒有明確匹配))。
不過,這里有一些可行的方法:
首先,使用型別引數而不是型別成員:
// (Steps as above)
// The Tree
sealed trait Tree[PathType <: Step]
// type alias to keep parameter lists simpler
// where we do not care..
type AnyTree = Tree[_ <: Step]
case class Leaf() extends Tree[LeafStep]
case class Horizontal(left: AnyTree, right: AnyTree) extends Tree[HorizontalStep]
case class Vertical(up: AnyTree, down: AnyTree) extends Tree[VerticalStep]
然后,這將完全型別檢查:
def callback[S <: Step, F <: Tree[S]](from: F, to: AnyTree)(step: S) = {
def horizontalCallback(from: Horizontal)(step: HorizontalStep) = {
step match {
case Left => ??? // do useful stuff here
case Right => ??? // do other useful stuff here
}
}
from match {
case f: Horizontal =>
horizontalCallback(f)(step)
case _ =>
()
}
}
令人困惑的是,如果像這樣直接將它放在外部匹配中,編譯器將無法正確檢查步驟上的模式匹配(編輯:這僅適用于2.12,其中這不會給出“匹配可能不是詳盡的”警告 - 對于 2.13 或 3.2,這檢查正常):
def callback[S <: Step, F <: Tree[S]](from: F, to: AnyTree)(step: S) = {
from match {
case f: Horizontal =>
step match {
case Left => ??? // do useful stuff here
case Right => ??? // do other useful stuff here
}
case _ =>
()
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/508566.html
