Livedoorブログからの移動

scalaのcollectionで躓いたのでメモ.

普通, collectionを何も考えず使う際は, immutableなものとして考えると思う(し, 実際immutableなものが作られる).
しかし, scala.collectionパッケージ内のtraitは, mutableなものも含むため, 型パラメータが非変なものとして設定されている. すなわち, 以下のようなプログラムはエラーを吐く. これは, Set[String]からSet[Any]への変換ができないため.
def func(s: Set[Any]) = println(s)
val s = Set("test")
func(s)
ちなみにエラー内容はこの通り.
:10: error: type mismatch;
 found   : scala.collection.immutable.Set[String]
 required: Set[Any]
Note: String <: Any, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
              func(s)
                   ^
これを解決する手段は大きく分けて3つ.
  1. エラーメッセージにあるとおり, funcに型パラメータを持たせること.
    def func[E <: Any](i: Set[E]) = println(i)
    順当な手法だけど, 正直いちいち型パラメータを作るのが面倒くさくなってくるのも事実.
  2. 呼び出し側でtoSetを呼び出す.
    func(s.toSet)
    こうすると, toSetが勝手に推論してSet[String]にしてくれるのでエラーを吐かなくなる.
    利点としてはエラー吐いた場所にtoSetを追加していけばいいので解決が簡単だという点, 欠点としては見栄えが悪い点.
  3. 以下のように暗黙変換を用意する.
    implicit def set2Set[A, B >: A](i: Set[A]): Set[B] = i.toSet
    何も考えずとも良くなる反面, 下手するとmutableなcollectionでの変換をしかねないので注意が必要.
    ただし, 自分しか使わない小規模なプログラムなら先頭にこれらを書いておけば記述が短くなると思う.

ただし, Listなど一部のimmutableにしかないcollectionは, 共変であり, 上述のようなことをしなくても変換が勝手に行われる(List以外にあるのかは知らない).
困ったらとりあえずListを使っておけば後が楽なのだろうか?