ちょっと遊んでみました。
scala> trait A; case class B(str: String) extends A; case class C(int: Int) extends A; case class D(str: String); defined trait A defined class B defined class C defined class D scala> val a = Seq(B("hoge"), C(1)) a: Seq[Product with Serializable with A] = List(B(hoge), C(1)) // BもCもAを継承しているのでSeq[A]になる。 scala> val b: Seq[A] = Seq(B("hoge"), C(1)) b: Seq[A] = List(B(hoge), C(1)) scala> val c = Seq(C(1), C(2)) c: Seq[C] = List(C(1), C(2)) // mapを使うとSeqの型がProduct with Serializable with Aになる。 scala> c.map(x => x match { | case x if x.int < 2 => new B(x.int.toString) | case _ => new C(x.int + 1) | }) res9: Seq[Product with Serializable with A] = List(B(1), C(3)) // DはAを継承していないので、エラーになる scala> val d: Seq[A] = Seq(B("hoge"), C(1), D("fuga")) // error <console>:17: error: type mismatch; found : D required: A val d: Seq[A] = Seq(B("hoge"), C(1), D("fuga")) // error ^ // foldLeftを使うとSeq[A]になる scala> c.foldLeft(Seq[A]())((z, x) => x match{ | case x if x.int < 2 => z :+ new B(x.int.toString) | case _ => z :+ new C(x.int + 1) | }) res11: Seq[A] = List(B(1), C(3))
Seqのmapの定義ですが、Thatが共変で、Thatが下限境界であるので、Seqのmapで複数の別々の型に変換すると、一番基底の型に引っ張られる形となるっぽいです。
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = { def builder = { // extracted to keep method size under 35 bytes, so that it can be JIT-inlined val b = bf(repr) b.sizeHint(this) b } val b = builder for (x <- this) b += f(x) b.result }