2012年9月13日木曜日

Scala Tips / Scala 2.10味見(9) - Try

プログラミング言語もバージョンが上がって新機能が追加されることによって、多かれ少なかれプログラミング・スタイルが更新されていきます。1バージョン切り替え毎に、マイナー更新レベルなのか、新しいバージョンに全面的に乗り換えて行かないといけないか選択が迫られることになります。

Scala 2.10は、InterpolationやType Dynamic、Reflection、macro(これはexperimentalですが)など内部DSLの構築方法を大きく変える機能が満載なので、全面的に乗り換えていくバージョンと考えてよいと思います。

そういった観点からも見逃せないのがscala.util.Tryです。Tryは正常状態と異常状態の2つの状態による計算文脈を提供するモナドです。

Scala 2.10.0-M7ではFutureでも計算結果通知に(元々のEitherから変更されて)Tryを使うようになっており、異常系を扱う処理ではTryが中心的なクラスになっています。

異常系処理はプログラム全体で使用する基本中の基本処理なので、ここの処理の中心にTryを据えると、今までとプログラミングスタイルが大きく変わってくることになります。

つまりTryは一見地味ですが、Scala 2.10のプログラミングスタイルを考える上で外せない機能ということです。

準備

準備として奇数の時に例外を投げる関数を用意します。

def even(v: Int) = {
  if (v % 2 == 1) throw new IllegalStateException("odd")
  else v
}

使い方

Tryの基本的な使い方は、以下のようにtry文の代わりに使います。try文ではcatchまたはfinallyが必須ですが、Tryでは実行結果がTryオブジェクトとして返ってきます。

import scala.util.Try

val a: Try[Int] = Try {
  even(10)
}

val b: Try[Int] = Try {
  even(11)
}

正常系の処理結果は以下になります。

a: scala.util.Try[Int] = Success(10)

異常系の処理結果は以下になります。

b: scala.util.Try[Int] = Failure(java.lang.IllegalStateException: odd)
allCatch & Either

例外処理はscala.util.control.ExceptionのallCatchやcachingメソッドとOptionやEitherを組合せて使うのが定番のイディオムになっていました。

allCatchとEitherを組み合わせると以下になります。

import scala.util.control.Exception.allCatch

val a: Either[Throwable, Int] = allCatch either {
  even(10)
}

val b: Either[Throwable, Int] = allCatch either {
  even(11)
}

正常系の処理結果は以下になります。

a: Either[Throwable,Int] = Right(10)

異常系の処理結果は以下になります。

b: Either[Throwable,Int] = Left(java.lang.IllegalStateException: odd)

実行結果は、結果を格納するオブジェクトがTryとEitherで違うだけで、ほとんど同じに見えます。

しかし、Eitherは直和を表現するための汎用オブジェクト、Tryは正常系/異常系の両方を取り扱う計算結果を表現する専用オブジェクトなので、使い勝手には相当の開きがあります。

次回はこのあたりの違いを調べてみる予定です。

ノート

TryとallCatchの細かい振舞いの違いの一つに受け取れる例外の種類があります。allCatchは文字通りすべて受け取るのに対して、Tryはscala.util.control.NonFatalに分類されるもののみになっています。このあたりの話題もいずれ取り上げたいと思います。

諸元

  • Scala 2.10.0-M7

0 件のコメント:

コメントを投稿