2012年4月19日木曜日

Scala Tips / Validation (4) - map

ValidationはScalazが提供する成功/失敗の計算文脈を提供するモナドです。

Validationを使ってOptionと同様の成功/失敗の計算文脈上でのMonadicプログラミングをすることができます。

今回は「Option(3)」や「Either」で扱った課題のValidation版を考えてみます。

条件結果
Validation[NonEmptyList[Throwable], B]がSuccess[NonEmptyList[Throwable], B]Success[NonEmptyList[Throwable], C]
Validation[NonEmptyList[Throwable], B]がFailure[NonEmptyList[Throwable], B]Failure[NonEmptyList[Throwable], C]

大枠ではB→Cの演算を行いたいわけですが、これをValidation[NonEmptyList[Throwable], B→C]の文脈の上で行うわけです。

以下ではValidation[NonEmptyList[Throwable], Int]をValidation[NonEmptyList[Throwable], String]に変換するプログラムを考えます。なお、Validation[NonEmptyList[Throwable], Int]はValidationNEL[Throwable, Int]と同等なので、可能な場合はValidationNELの方の表記を用います。

(分類の基準)

Java風

if式でValidation#isSuccessメソッドを使ってSuccessとFailureの判定をして、処理を切り分ける事ができます。Failureの場合にキャスト(asInstanceOf)を使うことになるので避けたい用法です。

def f(a: ValidationNEL[Throwable, Int]): ValidationNEL[Throwable, String] = {
  if (a.isSuccess) {
    Success(a.value.toString)
  } else {
    a.asInstanceOf[ValidationNEL[Throwable, String]]
  }
}

Scala風

match式を使うとSuccessとFailureのパターンマッチングで綺麗に書くことができます。ただし、Scala的には「Failure(b)の場合はFailure(b)」というロジックを書くのが悔しい。

def f(a: ValidationNEL[Throwable, Int]): ValidationNEL[Throwable, String] = {
  a match {
    case Success(b) => Success(b.toString)
    case Failure(b) => Failure(b)
  }
}

Scala

Option」や「Either」と同様に成功の文脈上で値の変換を行うにはmapメソッドを使用します。

def f(a: ValidationNEL[Throwable, Int]): ValidationNEL[Throwable, String] = {
  a.map(_.toString)
}

失敗の文脈であるFailureの場合は自動的に引き継がれるのがミソです。

Scalaz

Scalazでは、mapメソッドの別名として「∘」(Unicode 2218)を使用することができます。

def f(a: ValidationNEL[Throwable, Int]): ValidationNEL[Throwable, String] = {
  a ∘ (_.toString)
}

ただし、flatMap「∗」(Unicode 2217)やcontramap「∙」(Unicode 2219)と紛らわしいのですし、記号の入力も簡単ではないので、無理をして使うほどのことはなさそうです。

ノート

ValidationとOptionとの違いは、Validationはエラー時の情報を保持しておける点です。

Eitherも右側を成功、左側を失敗という運用でエラー情報を保持する運用ができますが(「Either」, 「Either(2) - flatMap」)、この運用とほとんど同じことができます。

EitherとValidationの違いは以下の点です。

  • EitherはLeft/Rightという汎用的なメカニズムを運用で成功/失敗の文脈として使用しているが、ValidationはSuccess/Failureとして明に成功/失敗の文脈の提供している。
  • Validationはエラー側の情報がモノイドとなっており、エラー情報を追加していくメカニズムを持っている。
記号

Scalazでは、mapメソッドの別名として「∘」(Unicode 2218)、flatMap「∗」(Unicode 2217)やcontramap「∙」(Unicode 2219)といった記号を用いることができることを紹介しました。フォントの関係で「∗」「∘」「∙」の区別がつきにくいのでmapに関しては個人的には使うつもりはありませんが、読めるようになっておく必要はあります。

Scalazで使用できる記号は以下のページが詳しいです。

また、Scalazの記号を入力するためのキー入力のカスタマイズ方法もあるようです。

諸元

  • Scala 2.9.2
  • Scalaz 6.0.4

0 件のコメント:

コメントを投稿