私は言語を学ぶために Scala で小さなレンダリング エンジンに取り組んでいます。エンジンのアーキテクチャは特性に強く基づいているため、必要に応じてパイプラインの一部を追加および削除できます。特徴のいくつかは次のとおりです。
(更新: いくつかのタイプ情報を修正しました)
trait Composable { val parent:DisplayObject with Composite = null }
trait Composite extends Composable { val children:ArrayBuffer[DisplayObject with Composable] = ArrayBuffer() }
trait Position { var x = 0.0d; var y = 0.0d }
...
DisplayObject
これらの特性を構成するために使用される空のクラスです。
私のパイプラインのステップの 1 つは、各オブジェクトの階層をフラット化することです。これでの私の最初のショットは次のとおりでした:(更新:本文を追加しました)
def flatten(root:DisplayObject with Composite) : ArrayBuffer[DisplayObject] =
{
def traverse(composite:Composite, acc:ArrayBuffer[DisplayObject])
{
acc += composite
for(composable <- composite.children)
{
composable match {
case com:Composite => traverse(com, acc)
case _ => acc += composable
}
}
}
val flat = new ArrayBuffer[DisplayObject]
traverse(root, flat)
flat
}
}
これは問題ありませんが、この関数を呼び出すと、多くの型情報が失われます。
val root = new DisplayObject with Position with Composite
root.children += new DisplayObject with Composable with Position
val list = flatten(root)
のタイプlist
は現在List[DisplayObject]
です。位置情報が失われています。そこで、ジェネリックをミックスに追加することを考えました:(更新:追加された本体)
def genericFlatten[T](root:T with Composite) : ArrayBuffer[T] =
{
def traverse(composite:T with Composite, acc:ArrayBuffer[T])
{
acc += composite
for(composable <- composite.children)
{
composable match {
case com:T with Composite => traverse(com, acc)
case composable:T with Composable => acc += composable
}
}
}
val flat = new ArrayBuffer[T]
traverse(root, flat)
flat
}
ただし、これを呼び出すと、この奇妙な結果が得られます。返されるリストのタイプは nowList[DisplayObject with Position with Composite]
です。これは間違っています。これは、ツリーの一部の子 (リーフ) が Composite トレイトを持たないためです。タイプ T が に推論されることを期待していましたDisplayObject with Position
。いいえ?