我正在尝试编写一些代码,以便轻松链接返回Scalaz Validation类型的函数.我试图编写的一种方法类似于Validation.flatMap(我要调用的短路验证)andPipe.另一个类似于|@|on ApplicativeBuilder(累积错误),除了它只返回Success我将调用的最终类型andPass
假设我有功能:
def allDigits: (String) => ValidationNEL[String, String]
def maxSizeOfTen: (String) => ValidationNEL[String, String]
def toInt: (String) => ValidationNEL[String, Int]
Run Code Online (Sandbox Code Playgroud)
作为一个例子,我想首先将输入String传递给allDigits和maxSizeOf10.如果存在故障,则应通过不调用toInt函数来短路,并返回发生的一个或两个故障.如果成功,我想将Success值传递给toInt函数.从那里,它将成功输出值为Int,或者它将无法仅从toInt返回验证失败.
def intInput: (String) => ValidationNEL[String,Int] = (allDigits andPass maxSizeOfTen) andPipe toInt
Run Code Online (Sandbox Code Playgroud)
如果没有下面的附加实现,有没有办法做到这一点?
这是我的实施:
trait ValidationFuncPimp[E,A,B] {
val f: (A) => Validation[E, B]
/** If this validation passes, pass to f2, otherwise fail without accumulating. */
def andPipe[C](f2: (B) => Validation[E,C]): (A) => Validation[E,C] = (a: A) => {
f(a) match {
case Success(x) => f2(x)
case Failure(x) => Failure(x)
}
}
/** Run this validation and the other validation, Success only if both are successful. Fail accumulating errors. */
def andPass[D](f2: (A) => Validation[E,D])(implicit S: Semigroup[E]): (A) => Validation[E,D] = (a:A) => {
(f(a), f2(a)) match {
case (Success(x), Success(y)) => Success(y)
case (Failure(x), Success(y)) => Failure(x)
case (Success(x), Failure(y)) => Failure(y)
case (Failure(x), Failure(y)) => Failure(S.append(x, y))
}
}
}
implicit def toValidationFuncPimp[E,A,B](valFunc : (A) => Validation[E,B]): ValidationFuncPimp[E,A,B] = {
new ValidationFuncPimp[E,A,B] {
val f = valFunc
}
}
Run Code Online (Sandbox Code Playgroud)
我并没有声称这个答案肯定比drstevens更好,但它需要一个稍微不同的方法,不适合那里的评论.
首先是我们的验证方法(注意我已经改变了一点类型toInt,原因我将在下面解释):
import scalaz._, Scalaz._
def allDigits: (String) => ValidationNEL[String, String] =
s => if (s.forall(_.isDigit)) s.successNel else "Not all digits".failNel
def maxSizeOfTen: (String) => ValidationNEL[String, String] =
s => if (s.size <= 10) s.successNel else "Too big".failNel
def toInt(s: String) = try(s.toInt.right) catch {
case _: NumberFormatException => NonEmptyList("Still not an integer").left
}
Run Code Online (Sandbox Code Playgroud)
为方便起见,我将定义一个类型别名:
type ErrorsOr[+A] = NonEmptyList[String] \/ A
Run Code Online (Sandbox Code Playgroud)
现在我们刚刚得到了几个Kleisli箭头:
val validator = Kleisli[ErrorsOr, String, String](
allDigits.flatMap(x => maxSizeOfTen.map(x *> _)) andThen (_.disjunction)
)
val integerizer = Kleisli[ErrorsOr, String, Int](toInt)
Run Code Online (Sandbox Code Playgroud)
我们可以撰写:
val together = validator >>> integerizer
Run Code Online (Sandbox Code Playgroud)
并使用这样的:
scala> together("aaa")
res0: ErrorsOr[Int] = -\/(NonEmptyList(Not all digits))
scala> together("12345678900")
res1: ErrorsOr[Int] = -\/(NonEmptyList(Too big))
scala> together("12345678900a")
res2: ErrorsOr[Int] = -\/(NonEmptyList(Not all digits, Too big))
scala> together("123456789")
res3: ErrorsOr[Int] = \/-(123456789)
Run Code Online (Sandbox Code Playgroud)
使用flatMap非monadic的东西让我有点不舒服,并将我们的两种ValidationNEL方法结合到\/monad中的Kleisli箭头中- 这也是我们的字符串到整数转换的合适模型 - 对我来说感觉有点清洁.
| 归档时间: |
|
| 查看次数: |
2029 次 |
| 最近记录: |