2012年4月10日火曜日

Scala Tips / Either (19) - Optionと相互変換

EitherとOptionの相互変換のイディオムです。

EitherのRightを成功、Leftを失敗として扱うことを想定しています。逆の場合は、成否の判定を反転させてください。

(分類の基準)

Java風

Option→Either

OptionをEitherに変換する場合は、OptionのifDefinedメソッドでSome(成功の文脈)の判定を行い、Someの場合はその値をRightに、そうでない場合は失敗の内容を示す値(この場合はThrowable)をLeftに詰めます。

def f[T](o: Option[T], e: Throwable): Either[Throwable, T] = {
  if (o.isDefined) Right(o.get) else Left(e)
}

Optionでは、成功と失敗の切り分けはできますが、失敗の内容を示す値は保持していないので、外側から指定する必要があります。

Either→Option

EitherをOptionに変換する場合は、EitherのisRihtメソッドで成功(Right)であることを確認した後、rightメソッドで取得したRightProjectionのrightメソッドを用いて値を取り出し、Someに詰めます。EitherがLeftの場合は、Left側の値は捨ててNoneを返します。

def f[T](e: Either[Throwable, T]): Option[T] = {
  if (e.isRight) Some(e.right.get)
  else None
}

Scala風

Option→Either

OptionをEitherに変換する場合は、match式を用いて、SomeとNoneの判定を行い、Someの場合は格納されている値をRightに詰めます。Noneの場合は、失敗を示す値をLeftに詰めます。

def f[T](o: Option[T], e: Throwable): Either[Throwable, T] = {
  o match {
    case Some(s) => Right(s)
    case None => Left(e)
  }
}
Either→Option

EitherをOptionに変換する場合は、match式でLeftまたはRightを判定し、Rightの場合は取得した値をSomeに詰めます。Leftの場合は、Left側の値は捨ててNoneを返します。

def f[T](e: Either[Throwable, T]): Option[T] = {
  e match {
    case Right(s) => Some(s)
    case Left(_) => None
  }
}

Scala

Option→Either

OptionをEitherに変換するMonadicな演算はないと思います。Scala風で説明したmatch式方式を用いるとよいでしょう。

Either→Option

EitherをOptionに変換する場合、Eitherのfoldメソッドを使うことができます。

def f[T](e: Either[Throwable, T]): Option[T] = {
  e.fold(_ => None, Some(_))
}

Scalaz

Option→Either

Scalazでは、OptionをValidationに変換するtoSuccessメソッドが追加されています。ValidationはさらにEitherに変換するeitherメソッドを持っているので、これを連結して使用します。

def f[T](o: Option[T], e: Throwable): Either[Throwable, T] = {
  o.toSuccess(e).either
}
Either→Option

ScalazではEitherをOptionに変換する便利なメソッドは用意していません。Javaで説明したfoldメソッドを用いるとよいでしょう。

ノート

EitherとOptionの相互変換は以下の手法がよいでしょう。

Either→Option
Eitherのfoldメソッド (Scala)
Option→Either
Validation経由 (Scalaz)

性能を重視する場合はJava風、ScalaやScalazの技が思い浮かばなかった場合はScala風のmatch式が安全策です。

諸元

  • Scala 2.9.1
  • Scalaz 6.0.3

0 件のコメント:

コメントを投稿