私はScalaで(path-)依存型を使用していて、次のような場合にはうまく解決できませんでした。 依存型の階層をいくつか持たせたいとし、それぞれに "所有者"オブジェクトへの参照を戻したいとします。私はこのバックリファレンスが正しい「所有者」オブジェクトのいくつかのメソッドを呼び出せるようにしたい。それを行う正しい方法は何ですか?リファレンスバック付き依存型(Scala)
ここに小さな例があります。依存型がInner
の「基本」形質Outer
があります。ベースOuter
の特性は、従属型で機能するいくつかのメソッドdouble
を定義します。特定の従属クラスConcreteInner
を持つ特定のクラスConcreteOuter
もあり、その値には簡単なInt
が使用されています。
trait Outer {
outerSelf =>
trait BaseInner {
val outer: outerSelf.type = outerSelf
def asDependent: outer.Inner // #1
// def asDependent: outerSelf.Inner // #2
}
type Inner <: BaseInner
def double(inner: Inner): Inner
}
class ConcreteOuter extends Outer {
case class ConcreteInner(val v: Int) extends BaseInner {
override def asDependent = this
}
type Inner = ConcreteInner
def createInner(v: Int): Inner = new ConcreteInner(v)
override def double(inner: Inner): Inner = new ConcreteInner(2 * inner.v)
}
これまでのところとても良いです。今私はいくつかのInner
クラスのインスタンスしか持っていないが、対応するOuter
-instanceのインスタンスがあるコンテキストでそのdouble
メソッドを呼び出すことができると仮定します。たとえば、ちょうど他のいくつかの(独立した)状況で、元Outer.double
を呼び出し、別のdouble
メソッドを作成してみましょう:
object DepTest extends App {
//def double(inner: Outer#Inner) = inner.outer.double(inner) // #3
def double(inner: Outer#Inner) = inner.outer.double(inner.asDependent) // #4
val c1 = new ConcreteOuter
val i1 = c1.createInner(123)
val d1 = double(i1)
println(d1)
}
このコードはコンパイルされますが、asDependent
のかなり醜いハックが必要です。行#4の代わりに行#3を使用すると、コードはコンパイルされません。私はもう
def double(inner: Outer#Inner) = {
val outer = inner.outer
outer.double(inner.asDependent)
}
コードはコンパイルされません、次のようにライン#3を分割した場合、私はライン#2とライン#1を交換した場合また、さえasDependent
ハックは動作を停止します。
何とか時には、コンパイラはouterSelf
別名Inner
オブジェクトおよび「所有者」オブジェクトのouter
フィールドが同じものであることを知っていて、時にはそれがないと、時にコンパイラを説得する方法は明らかではないようなので、それが見えますそれらを同じものとして認識しません。
これを回避する方法はありますか?それとも、これは私の問題に対する完全に間違ったアプローチですか? (明らかに現実の世界では、DepTest.double
などのダムプロキシだけでなく、multiplyByPow2(val : Outer#Inner, exponent: Int)
などの上位レベル関数のライブラリを作成したい)