Scalaのtype、気になるvisibility

同僚がC++でテンプレートごりごり使って書いたコードをScalaに移植しようとして、type(typedefみたいなアレ)周りで変なものを発見したと教えてくれたのが13日の金曜日。気になったので自分でもいじってみました。

# Scala version 2.9.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_05)

class Foo[T] {
  val x = 10
  type tx = T
}

昔々、あるところにクラス Foo がありました。(ジェネリックな型であることは本題と関係なし)

class Bar1(foo: Foo[_]) {
  type tx = List[foo.tx]  // NG
}

Fooを引数に取るクラス。インスタンスの型に対応するコレクションを持つことにします。後々めんどくさいので別名をつけます。

 error: private value foo escapes its defining scope as part of type java.util.List[Bar1.this.foo.tx]

何か文句を言われました。privateなどの不可視なものに触ったときのエラーは value x in class Hoge cannot be accessed in Hoge なので、状況が違いそう。
escapes defining scope...のエラーメッセージでぐぐってみると、Programming ScalaのChapter 5に同じエラーメッセージがあります。アクセス不可能なスコープにあるクラスを継承しようとした場合のエラーのようです。

Workaround

エラーメッセージは、privateなものをpublicにしているのが問題だと言っているように見えるので、その通り直せばコンパイルは通りました。

class Bar2(val foo: Foo[_]) {
  type tx = List[foo.tx]
}

class Bar3(foo: Foo[_]) {
  private type tx = List[foo.tx]
}

ただし、Bar2はfooが外部に公開されているのでイマイチです。Bar3はtxがprivateになっているのでイマイチです。どちらも意味が変わってしまっています。

class Bar4(foo: Foo[_]) {
  private type tx = List[foo.tx]
  type public_tx = tx  // NG
}

List[foo.tx]を外側に出そうとしたらまた怒られました。
でも実は生のfoo.txだと大丈夫みたいで…

class Bar5(foo: Foo[_]) {
  private type tx = foo.tx
  type public_tx = tx
}

class Bar6(foo: Foo[_]) {
  private type tx = foo.tx
  type public_tx = List[tx]
}

外側から触っても大丈夫のようです。

scala> val foo6 = new Bar6(new Foo[Int])
foo6: Bar6 = Bar6@243b31a6

scala> var x: bar6.public_tx = _
x: bar6.public_tx = null

scala> val z = new HashMap[Int, bar6.public_tx]
z: java.util.HashMap[Int,bar6.public_tx] = {}


コンパイルのためだけに一行増やすなんてどうかと思いつつも、とりあえず解決。いいのかなあこれで。
objectの中にtypeを定義した場合は、好き勝手にアクセスできるpublicなイメージなので、なんだかもやもやします。