List[T[_]].sequence と同じように List[Either[_, _]].sequence したい

公開:2020/12/15
更新:2020/12/16
1 min読了の目安(約900字TECH技術記事

cats では List[T[U]] のような List の入れ子が 型パラメータを一つだけ取る(注) 場合 .sequence すれば T[List[U]] に変換できる

import cats.implicit._
val xs: List[Future[String]] = ...
xs.sequence // Future[List[String]]

注) 厳密には Apprecative[_] を継承していないといけない

けど 2.12以前のscala だと Either は型パラメーターが二つあるのでそのままでは .sequence できない

val xs: List[Either[E, String]] = ...
xs.sequence // compiler error!

一応型エイリアスをつけてコンパイルを通すことはできるがめんどくさい

type MyEither[T] = Either[MyError, T]
val xs: List[MyEither[String]] = ...
xs.sequence // MyEither[List[String]]

ところがどっこい、コンパイラオプションを追加するだけであら不思議

build.sbt

scalacOptions += "-Ypartial-unification"
val xs: List[Either[MyError, String]] = ...
xs.sequence // Future[Either[MyError, String]]

.sequence がいい感じに使えるのだ

"partial-unification" って何?

複数型パラメータを取る型があったとき、コンパイラに型パラメータの一番右を唯一の型パラメータと同様にみなすようにするオプション。 Either のように本命が右側の型パラメータである型には有効だが、本命が左側の型パラメータであるような型では誤った推論になるので注意が必要。