抽象型で抽象再帰データ構造を定義したいと思います。このようなもの :
case class ParentA( name : String, children : List[ParentA] ) extends Parent {
type PARENT = ParentA
}
case class ParentB( name : String, children : List[ParentB] ) extends Parent {
type PARENT = ParentB
}
sealed abstract class Parent {
// we'd like to Define Parent as a PARENT
// something like:
// this : PARENT =>
type PARENT <: Parent
val name : String
val children : List[PARENT]
def findParent(name:String) : Option[PARENT] = {
if( name == this.name ) {
Some(this) // ouch
} else {
// re-ouch
children.flatMap( f => f.findParent(name) )
}
}
}
val a2a : ParentA = ParentA("a",List(ParentA("a1",Nil),ParentA("a2",List(ParentA("a2a",Nil))))).findParent("a2a").get
もちろん、これはコンパイルされません。コンパイラは、Parent.thisがPARENTであると推測できないためです。
error: type mismatch;
found : Parent.this.type (with underlying type this.Parent)
required: Parent.this.PARENT
Some(this)
そしてまた
error: type mismatch;
found : List[Parent.this.PARENT#PARENT]
required: Option[Parent.this.PARENT]
children.flatMap( f => f.findParent(name) )
私はあちこちでキャストすることでそれを回避することができますが、親が親であることをコンパイラに伝えることができる方が良いでしょう。または多分私は何かが欠けています:)
編集-ジェネリック
ジェネリックはオプションではないことを言及するのを忘れました。この例は、実際には、より微妙な問題の単純化されたバージョンです。ジェネリックを使用すると、プログラムの2次成長につながります。ジェネリックスが常に実行可能な代替手段であるとは限らない理由を説明するリンクは次のとおりです。Scala:抽象型とジェネリックス。
基本的に、私は抽象型を使用する方が良いです-あるいは抽象型もジェネリックも使用しないでください-そしてキャストします。