Pull Requestでレビューをしていてこういう実装を見かけた。
fooで失敗したらbar, barで失敗したらbazを呼び出したい。
def foo(s: String): String = ??? def bar(s: String): String = ??? def baz(s: String): String = ??? val input: String = "awesome text" val result: String = Try { foo(input) } match { case Success(value) => value case Failure(_) => { Try { bar(input) } match { case Success(value) => value case Failure(_) => { Try { baz(input) } match { case Success(value) => value case Failure(error) => throw error } } } } }
意図は分かりやすいがインデントが深いので以下のように修正した。
Tryを合成しているということがひと目でわかるようになった。
orElse
は getOrElse
と違い中身を取り出さずにデフォルトの挙動を定義する。
今回のようにデフォルトの挙動を多段に組んで最後に取り出したいようなケースでは有用である。
val resultTry: Try[String] = Try(foo(input)) .orElse(Try(bar(input))) .orElse(Try(baz(input))) val result: String = resultTry match { case Success(value) => value case Failure(error) => throw error }
上記以外にもOption, EitherにもorElseメソッドがあり、Optionのscaladocが分かりやすいので引用。
ちょうど最初に挙げた例のように"パターンマッチで取り出すのと同じですよ" と書いてくれている。
/** Returns this $option if it is nonempty, * otherwise return the result of evaluating `alternative`. * * This is equivalent to: * {{{ * option match { * case Some(x) => Some(x) * case None => alternative * } * }}} * @param alternative the alternative expression. */ @inline final def orElse[B >: A](alternative: => Option[B]): Option[B] = if (isEmpty) alternative else this
これまであまり使うシチュエーションが無く初めて使ったが、
Javaの実装をラップしているときなんかは割と使うことが多いのだろうと感じる。
なお、orElseの引数はTry,Option,Eitherでそれぞれ名前が異なっているがやってることはどれも同じようなものである。
Try.orElse[U >: T](default: => Try[U]): Try[U]
Option.orElse[B >: A](alternative: => Option[B]): Option[B]
Either.orElse[A1 >: A, B1 >: B](or: => Either[A1, B1]): Either[A1, B1]