共変とか反変とかわからないので躓いている

こんなクラスを作ってみた

scala> case class K[A](l:List[A])


そしてこんな関数を作ってみる。しかし実行してみるとうまくいかない。

scala> def returnK(a:A):K[A] = K[A](List(a))
returnK: (a: A)K[A]

scala> returnK(1)
 <console>:15: error: type mismatch;
  found   : Int(1)
  required: A
               returnK(1)
                       ~

この場合は、引数の型が予めわかってるので、以下のようにすればうまくいく。

scala> def returnK[A](a:A):K[A] = K[A](List(a))
returnK: [A](a: A)K[A]

scala> returnK[Int](1)
res29: K[Int] = K(List(1))

だがしかし、であります。次のような、各行に文字列もしくは数値のいずれか一つが入っているテキストファイルを想定してみる。

tanaka
12
suzuki
yoshida
10
sato
yamada
ito
191

一行ごとに読み込んで、文字列の場合はK[String]を、数値の場合はK[Int]を返す、というような関数を考えてみる。すると、事前にどのような値が返ってくるかわからないので、上で定義したようなreturnK[A]は使えないように思う。かといって、以下のように戻り値にK[Any]を使うのも気持ちが悪い。

scala> def returnKAny(a:Any):K[Any] = K(List(a))
returnKAny: (a: Any)K[Any]

scala> returnKAny("test")
res46: K[Any] = K(List(test))

scala> returnKAny(1)
res47: K[Any] = K(List(1))


なにかいい方法はないものだろうか。



9:28 追記


実は悩むことなんかなかった。returnKを呼ぶときにわざわざreturnK[Int]とすることなく、単純に

returnK(1)
res4: K[Int] = K(List(1))

とするだけで良かった。

しかしまた新な問題が出てきた。次のステップとして、returnKの戻りを別の関数の戻り値に使うことにする。これがまた難解だ。

scala> def get(i:Int) = i match {
     | case 0 => returnK(1)
     | case _ => returnK("a")
     | }
get: (i: Int)K[_ >: Int with String]

scala> get(0)
res12: K[_ >: Int with String] = K(List(1))

scala> get(1)
res13: K[_ >: Int with String] = K(List(a))

これはこれで動く。だが、もどりがK[_ >: Int with String]になるのが気に入らない。うーん。



14:42 追記


returnKを呼び出しているgetにもジェネリクス(?)型パラメーターを付けてみた。

scala> def get[A >: Any](i:Int):K[A] = i match {case 0 => K(List(0)); case _ => K(List("other"))}
get: [A >: Any](i:Int)K[A]

scala> get(0)
res3: K[Any] = K(List(0))

scala> get(1)
res4: K[Any] = K(List(other))

相変わらずK[Any]で返ってくる。なかなかうまく行かないものです。


2015/05/20 15:14 追記


いろいろ考え込んでいたが、大いに間違いであることがわかった。
こっち参照--> 返ってくる型がわからないときは、Anyを使う? - 趣味プログラマがまれになんかしたことの記録