パス依存型(経路依存型)
大前提
Scalaでは、クラスやオブジェクト、トレイトの内部でもまた、クラスやオブジェクト、トレイト、そしてtype(型メンバ)を定義できる
べんり
code:scala
class Foo:
object Bar:
val x = 42
小前提
この内部にある型の等価性をどのように判定するのか?
例えば、val a = new Foo()でval b = new Foo()のとき、a.Bar == b.Barと考えていいのだろうか?
Scalaではこう考える
new Foo().Bar == new Foo().Barではない
val a = new Foo()のとき、a.Bar == a.Barである
つまり、Barの型情報にはその外側のFooのインスタンスとしての情報が加味されている
もうすこし具体的には・・・
「別のインスタンスから生まれる型は、その定義が同じでも別物とみなす」
型が違うので、互いに代入することも不可能
code:scala
class Foo:
object Bar
Foo().Bar == Foo().Bar // => これはfalse。別のインスタンスから生まれているため
object Fizz:
object Buzz
Fizz.Buzz == Fizz.Buzz // => これはtrue。同じインスタンスから生まれているため
これは型が生成される経路に判定が依存しているように見えるため、「path-dependent types」「パス依存型」「経路依存型」などと呼ばれる。
(path-dependent) typesなのであって、path-(dependent types)ではない
プログラミング言語Idrisなどが持っている「依存型」とはまったく関係がないことに注意
たまに混同している記事がある
何が便利か
型メンバにより、具体的な型の詳細を値側に押し付けて、抽象的な処理を記載できるようになる
パス依存型により、よそのインスタンスの内部の値に対するバリアを作ることができる
code:scala
class Proc:
self =>
opaque type Stigma = self.type
def spawn(): Proc & Stigma = new Proc().asInstanceOfProc & Stigma def kill(p: Proc & Stigma) = println("killing")
code:scala
val proc1 = Proc()
val proc2 = Proc()
val proc1Child = proc1.spawn()
proc1.kill(proc1Child) // ok
proc2.kill(proc1Child) // Compile error!!
上掲の例では、自らがspawnしたProcしかkill()できなくしている
See Also