by shigemk2

当面は技術的なことしか書かない

Seqでちょっと遊んでみましょう

ちょっと遊んでみました。

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
  }