[cats] ValidatedでList[A]のバリデーションをする
cats.data.Validatedによってリストをバリデーションする場合は`Semigroup#combineAllOption`および`Monoid#combineAll`によってバリデーション結果を結合できます。
はじめに
cats.data.Validatedを使ってList[A]
の要素をバリデーションし結果をバリデーション済みのリストValidatedNec[E, List[A]]
として得る方法を試してみました。
作戦
ValidatedNec[+E, +A]
に対してAがMonoidまたはSemigroupであれば、ValidatedNecにもSemigroupおよびMonoidが得られます。そしてListはMonoidなのでMonoid#combineAll
によって結合できます。
コード例
コードは以下のようになります。この例ではList[String]
を各文字列の長さを基準にバリデーションしています。
package example import cats.data._ import cats.implicits._ import cats.kernel.{Monoid, Semigroup} package object validation { type ValidationError = String type ValidatedResult[A] = ValidatedNec[ValidationError, A] type ValidatedStrings = ValidatedResult[List[String]] def validateStrings(ss: String*): ValidatedResult[List[String]] = Monoid[ValidatedResult[List[String]]].combineAll(ss.map(validateLength)) def validateLength(s: String): ValidatedResult[List[String]] = if (s.length >= 5) List(s).validNec else s"too short: $s".invalidNec //println(validateStrings(Range(1, 10).map("a".repeat): _*)) //Invalid(Chain(too short: a, too short: aa, too short: aaa, too short: aaaa)) }
Semigroupの場合
ListはMonoidなのでcombineAllによって常に結果のリストが得られますが、Semigroupの場合はそうとは限りません。実際NonEmptyListで確かめてみます。
package example import cats.data._ import cats.implicits._ import cats.kernel.{Monoid, Semigroup} package object validation { type ValidationError = String type ValidatedResult[A] = ValidatedNec[ValidationError, A] type ValidatedStrings = ValidatedResult[List[String]] type ValidatedNonEmptyList = ValidatedResult[NonEmptyList[String]] implicitly[Semigroup[ValidatedStrings]] implicitly[Monoid[ValidatedStrings]] implicitly[Semigroup[ValidatedNonEmptyList]] // 以下はコンパイルエラー // implicitly[Monoid[ValidatedNonEmptyList]] //could not find implicit value for parameter e: cats.kernel.Monoid[example.validation.package.ValidatedNonEmptyList] //implicitly[Monoid[ValidatedNonEmptyList]] }
まとめ
cats.data.Validatedによってリストをバリデーションする場合はSemigroup#combineAllOption
およびMonoid#combineAll
によってバリデーション結果を結合できます。