ともちんの Tech ブログ

Seq[Option[A]].flatten はできるが Seq[Either[A, B]].flatten はできない

概要

Seq[Option[T]].flatten はできるが、Seq[Either[A, B]].flatten とすることはできない。

flatten メソッドは、以下のように入れ子になったコレクションや Option、Try などを平滑化することができる。

また、Option を Seq で包んだ Seq[Option[T]] は flatten によって平滑化できる。

その逆 Option[Seq[T]] はできない。

なお、Either や Try を Seq で包んだ Seq[Either[A, B]] や Seq[Try[T]] は flatten によって平滑化できない。

理由

Seq[Option[T]].flatten ができる理由は、flatten の定義にある。

結論から述べると、Seq.flatten メソッドは、TraversableOnce への暗黙的な型変換が定義されていれば実行することができる

すなわち、Option[T] には TraversableOnce への暗黙の型変換が定義されているが、Try[T] と Either[A, B] には定義されていない。

Seq.flatten の定義

では、Seq.flatten の定義を見てみよう。このメソッドは、GenericTraversableTemplate で定義されている

genericBuildersequential はそれぞれ以下のように定義されている。

genericBuilder は Seq を生成するビルダーである。

sequential は呼び出し元の Seq である。

asTraversable は、コレクションの要素 (型 A) を平滑化された要素のコレクション (型 GenTraversableOnce[B]) に変換する暗黙的な関数である。

この asTraversable が option2Iterable にあたる暗黙の型変換である。

Seq[Option[T]].flatten について

ここで、Seq(Some(1), None, Some(2)).flatten をその定義と型を照らし合わせてみよう。

まず、呼び出し元が Seq[Option[Int]] であることから、型 A は Option[Int]、型 B は Int、型 CC は Seq である。

asTraversable は、Option[Int] を GenTraversableOnce[Int] に変換する関数である。これは option2Iterable: Option[Int] -> Iterable[A] である。

for (xs <- sequential) によって Seq の各要素 xs: Option[Int] に対して処理がなされる。

asTraversable(xs).seq は Option[Int] を Iterable[Int] に変換する。それをビルダーに追加する。

このビルダー genericBuilder[Int] は Seq を生成するので、結果として得られるのは Seq[Int] である。

まとめ

  • Seq[Option[T]].flatten によって、Seq[T] を得ることができる。
  • その理由は、Option に Iterable への暗黙の型変換が定義されているからである。
  • Try や Either には、TraversableOnce への暗黙の型変換が定義されていない。そのため、Seq.flatten を用いることができない。