この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
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
によってバリデーション結果を結合できます。