Validationの集りに対して畳込みを行う場合、Failureの扱いには以下の2つの選択肢があります。
- Failureが一つでもあれば畳込み全体をFailureにする。
- Failureは飛ばしてSuccessのみを畳込む。
前回は、「Failureは飛ばしてSuccessのみを畳込む」の処理について、Validationが格納する要素がMonoidで、畳込み演算がMonoidの加算処理という条件で実装を行いました。このケースでは、foldメソッドは使う必要はあるものの、畳込み演算そのものはValidationの「>>*<<」メソッドを使って簡潔に記述することができました。
今回は「格納する要素がMonoidで、畳み込み演算がMonoidの加算処理」という条件を外して、より汎用的な処理方法について考えます。
課題
Stringを格納したValidationのリスト上で、StringをIntに変換後のIntを加算で畳み込んだ結果の値を格納したValidationを生成します。ただし、ValidationがFailureがあった場合には、Failureは飛ばしてSuccessのものだけを畳み込み結果をSuccessにします。
具体的には、以下の関数を作ります。
- f(a: List[ValidationNEL[Throwable, String]): ValidationNEL[Throwable, Int]
実装
課題の実装は以下になります。
def f(a: List[ValidationNEL[Throwable, String]]): ValidationNEL[Throwable, Int] = { type VNT[A] = ValidationNEL[Throwable, A] a.foldLeft(0.pure[VNT]) { (a, x) => x.flatMap(_.parseInt) match { case Success(s) => a.map(_ + s) case Failure(e) => a } } }
かなりカスタムな処理になるので、foldLeftを使って地道にロジックを記述します。
初期値は「0.pure[VNT]」です。
畳込み演算のポイントは2つあります。一つはValidationの入れ子の捌き方です。Validation上のStringにparseIntメソッドを適用するとValidationが生成されるため、Validationの入れ子の中にStringが入ることになります。このValidationの入れ子を一つのValidationにまとめる必要があります。つまりValidation[String]→Validation[Validation[Int]]→Validation[Int]の変換を行うわけですが、これはString→Validation[Int]のmap演算とValidation[Validation[Int]]→Validation[Int]のjoin演算を連続実行したものです。さらにmap演算とjoin演算を合成したものがモナドのbind演算なので、このbind演算を行えばよいわけです。Scalaのbind演算はflatMapメソッドですから、flatMapメソッドでString→Validation[Int]を行うStringのparseIntメソッドを指定します。
もう一つは、flatMapメソッドの実行結果として得られるvalidation[Int]を、実際に畳込み込んでいく処理です。この処理が今回のテーマである「Failureは飛ばしてSuccessのみを畳込む」になります。ValidationのApplicative処理とは異なった処理をすることになるので、地道にmatch式で条件判定して以下の処理を記述します。
- Successなら、積算値のValidationに値を足し込む。(mapメソッドを使用)
- Failureなら、積算値をそのまま使う。(Failureの情報は捨てる)
入力がStringのListの場合
参考に、入力がStringを格納したValidationのListではなく、StringのListの場合を考えます。この場合は、前述のようにflatMapは使う必要はなく、parseIntメソッドの結果を直接match式で条件判定してケースごとの処理を記述します。
def f(a: List[String]): ValidationNEL[Throwable, Int] = { type VNT[A] = ValidationNEL[Throwable, A] a.foldLeft(0.pure[VNT]) { (a, x) => x.parseInt match { case Success(s) => a.map(_ + s) case Failure(e) => a } } }
諸元
- Scala 2.9.2
- Scalaz 6.0.4
0 件のコメント:
コメントを投稿