2012年4月18日水曜日

Scala Tips / Validation (3) - NonEmptyList

NonEmptyListは、Scalazの提供するデータ構造で、名前から分かるように空であることがないリストです。また、Listと同様にモノイドの性質を持っています。

NonEmptyListは、空でないことが保証されているので、データ構造の持つ値域がより明確になります。具体的には、リストに対する空判定が必要でないことが、プログラムから明確に読み取れるようになります。

ValidationのFailureは、格納するオブジェクトがモノイドの場合、エラー情報を積算していけるようになっています。Listを使ってもよいのですが、Failureを使う場合、エラーが必ず一つ以上あることが前提なので、空でないことが保証されているリストであるNonEmptyListを使うのがより適切です。

そのような事情もあり、ValidationはFailure側にNonEmptyListを使うのが定番のイディオムになっています。

今回は、NonEmptyListを使うValidationの生成方法をまとめてみました。

Failure側はThrowableのNonEmptyList、Success側はIntのValidationの生成方法について考えます。

Failure

正攻法

Failureを生成する正攻法は、Failureの引数にNonEmptyListを指定する方法です。Failure側とSuccess側の型パラメータを指定しないといけないのでちょっと大変です。

  1. def f(e: Throwable): Validation[NonEmptyList[Throwable], Int] = {  
  2.   Failure[NonEmptyList[Throwable], Int](NonEmptyList(e))  
  3. }  
nel関数

NonEmptyListの生成では、nel関数を用いることができます。NonEmptyListを直接使うより少しだけ短くなります。

  1. def f(e: Throwable): Validation[NonEmptyList[Throwable], Int] = {  
  2.   Failure[NonEmptyList[Throwable], Int](nel(e))  
  3. }  
wrapNelメソッド

任意のオブジェクトをNonEmptyList化するには、wrapNelメソッドを使うことができます。

  1. def f(e: Throwable): Validation[NonEmptyList[Throwable], Int] = {  
  2.   Failure[NonEmptyList[Throwable], Int](e.wrapNel)  
  3. }  
ValidationNEL

「Validation[NonEmptyList[X], Y]」の型は頻繁に用いられるので、この型の別名として「ValidationNEL[X, Y]」という型が定義されています。ValidationNELを使って書き換えると以下のようになります。

  1. def f(e: Throwable): ValidationNEL[Throwable, Int] = {  
  2.   Failure[NonEmptyList[Throwable], Int](e.wrapNel)  
  3. }  
failure関数

failure関数を使ってFailureを生成することができます。手間はFailureを直接使うのとあまり変わりません。

  1. def f(e: Throwable): ValidationNEL[Throwable, Int] = {  
  2.   failure[NonEmptyList[Throwable], Int](e.wrapNel)  
  3. }  
liftFailNelメソッド

Validation[Throwable, Int]がある状態で、これをNonEmptyList版にするには、liftFailNelメソッドを使います。

  1. def f(e: Throwable): ValidationNEL[Throwable, Int] = {  
  2.   e.fail[Int].liftFailNel  
  3. }  
failメソッド

任意のオブジェクトをfailメソッドを用いてFailureにすることができます。Success側の型を型パラメータで指定します。ValidationNELにしたいので、まずオブジェクトをwrapNelメソッドでNonEmptyListにした後に、failメソッドを適用しています。

  1. def f(e: Throwable): ValidationNEL[Throwable, Int] = {  
  2.   e.wrapNel.fail[Int]  
  3. }  
failNelメソッド

failNelメソッドを使えば、任意のオブジェクトをNonEmptyListのFailureにすることができます。

  1. def f(e: Throwable): ValidationNEL[Throwable, Int] = {  
  2.   e.failNel[Int]  
  3. }  

Success

正攻法

Successを生成する正攻法は、Successの引数にNonEmptyListを指定する方法です。こちらもFailure側とSuccess側の型パラメータを指定しないといけないのでちょっと大変です。

  1. def f(a: Int): ValidationNEL[Throwable, Int] = {  
  2.   Success[NonEmptyList[Throwable], Int](a)  
  3. }  
success関数

success関数を使ってSuccessを生成することができます。手間はSuccessを直接使うのとあまり変わりません。

  1. def f(a: Int): ValidationNEL[Throwable, Int] = {  
  2.   success[NonEmptyList[Throwable], Int](a)  
  3. }  
successメソッド

任意のオブジェクトをsuccessメソッドを用いてSuccessにすることができます。Failure側の型を型パラメータで指定します。

  1. def f(a: Int): ValidationNEL[Throwable, Int] = {  
  2.   a.success[NonEmptyList[Throwable]]  
  3. }  
liftFailNelメソッド

Validation[Throwable, Int]がある状態で、これをNonEmptyList版にするには、liftFailNelメソッドを使います。Successの場合も、Failureと同様に使用できます。

  1. def f(a: Int): ValidationNEL[Throwable, Int] = {  
  2.   a.success[Throwable].liftFailNel  
  3. }  
successNelメソッド

failNelメソッドを使えば、任意のオブジェクトをNonEmptyListのSuccessにすることができます。

  1. def f(a: Int): ValidationNEL[Throwable, Int] = {  
  2.   a.successNel[Throwable]  
  3. }  

ノート

Validationを使う場合は、かなりの確率でNonEmptyListとThrowableを併用することになるので「ValidationNEL[Throwable, T]」という形とこの型に対する操作はイディオムとして覚えておくとよいでしょう。

なお、ExceptionではなくThrowableを選んだのは、scala.util.control.Exceptionとの相性を考慮したためです。「Scala Tips / Either (5) - Exception」が参考になると思います。Validation編も整理する予定です。

記事では、ValidationNEL[Throwable, T]を生成する様々な方法を紹介しましたが、普通はfailNelメソッド、successNelメソッドを使うのがよいでしょう。その他の方法は、必要に応じて使い分けることになります。引き出しが多い方が応用が効くので、色々なルートを紹介しました。

諸元

  • Scala 2.9.2
  • Scalaz 6.0.4

0 件のコメント:

コメントを投稿