我想使用Scalaz进行验证,并希望能够在不同的上下文中重用验证功能。我是Scalaz btw的新手。

假设我有以下简单检查:

def checkDefined(xs: Option[String]): Validation[String, String] =
  xs.map(_.success).getOrElse("empty".fail)

def nonEmpty(str: String): Validation[String, String] =
  if (str.nonEmpty) str.success else "empty".fail

def int(str: String): Validation[String, Int] = ...


我希望能够构成验证,将其中一个的输出馈送到另一个中。我可以轻松地通过flatMap或via做到这一点,但是感觉必须有比这更好的方法。

for {
  v1 <- checkDefined(map.get("foo"))
  v2 <- nonEmpty(v1)
  v3 <- int(v2)
  v4 <- ...
} yield SomeCaseClass(v3, v4)


要么

val x1 = checkDefined(map get "foo").flatMap(nonEmpty).flatMap(int)
val x2 = check(...)

// How to combine x1 and x2?


Scalaz专家对此有何想法?

最佳答案

除了@oxbow_lakes建议的解决方案之外,您还可以使用Kleisli组合。

scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> def f: Int => Validation[String, Int] = i => if(i % 2 == 0) Success(i * 2) else    Failure("Odd!")
f: Int => scalaz.Validation[String,Int]

scala> def g: Int => Validation[String, Int] = i => if(i > 0) Success(i + 1) else Failure("Not positive!")
g: Int => scalaz.Validation[String,Int]

scala> type Va[+A] = Validation[String, A]
defined type alias Va

scala> import Validation.Monad._
import Validation.Monad._

scala> kleisli[Va, Int, Int](f) >=> kleisli[Va, Int, Int](g)
res0: scalaz.Kleisli[Va,Int,Int] = scalaz.Kleislis$$anon$1@4fae3fa6

scala> res0(11)
res1: Va[Int] = Failure(Odd!)

scala> res0(-4)
res2: Va[Int] = Failure(Not positive!)

scala> res0(4)
res3: Va[Int] = Success(9)




类型为A => M[B]的函数,其中M : Monad被称为Kleisli箭头。

您可以使用A => M[B]运算符组合两个Kleisli箭头B => M[C]A => M[C]以获得箭头>=>。这就是所谓的克莱氏成分。

表达式kleisli(f) >=> kleisli(g) >=> kleisli(h)等同于x => for(a <- f(x); b <- g(a); c <- h(b)) yield c,减去不必要的本地绑定。

07-26 03:25