Scalaz 使用 ValidationNel 失败缓慢
Scalaz fail slow using ValidationNel
我正在学习 Scala,今天遇到了使用 Scalaz ValidationNel
的 Fail Slow
机制,但是很难理解如何使用它。我正在阅读这些博客:Blog1 , I am reading this Whosebug post too: Whosebug 但对于非函数式程序员来说真的很难理解。有人可以提供一个简单的例子来说明如何在 Scala 中累积 ValidationNel
中的错误吗?对示例进行描述也会很有帮助。
使用您链接的博客中的示例
val sumV: ValidationNEL[String, Int] = for {
a <- 42.successNel[String]
b <- "Boo".failNel[Int]
c <- "Wah wah".failNel[Int] // by defn of flatMap, can't get here
} yield a + b + c
这是在使用 flatMap 将各种操作链接在一起。例如,42.successNel[String] 创建成功,而 "Boo".failNel[Int] 创建失败。 flatMap 在这里的工作方式是只有在成功时才继续进行下一个操作。所以这是一个 "fail fast" 操作 - 它将第一个失败收集到您的错误案例中并停止。
如果你想 "fail slow" - 即。收集所有可能的故障,您需要使用不同的方法。这就是 Applicative 的用武之地。
val yes = 3.14.successNel[String]
val doh = "Error".failNel[Double]
def addTwo(x: Double, y: Double) = x + y
(yes |@| yes)(addTwo) // Success(6.28)
(doh |@| doh)(addTwo) // Failure(NonEmptyList(Error, Error))
(a |@| b)(someFunctionOfTwoArgsHere)
- 这就是说“执行 'a' 操作,并执行 'b' 操作,如果两者都成功,则执行 someFunctionOfTwoArgsHere(a,b)。否则,采取任何失败并将它们组合。因此,如果 a 失败,但 b 成功,则验证失败,结果为 a 失败。如果 a AND b 失败,则验证失败,结果为 a 和 b 均失败.
上一个答案很好,但我知道你来自 OOP 范式,所以让我再举一个例子来比较这两种范式。
常用代码:
val a = "1"
val b = "aaa"
val c = "bbb"
def isAllDigits(x: String) = x forall Character.isDigit
def appendError(x: String, errors: mutable.Buffer[String]) = errors += s"$x is not number"
type Errors = NonEmptyList[String]
// disjunction \/ for fail fast
def toDigitFailFast(x: String): Errors \/ Int = {
if (isAllDigits(x)) {
x.toInt.right
} else {
s"$x is not number".wrapNel.left
}
}
// validation nel (non empty list) for fail slow
def toDigitFS(x: String): ValidationNel[String, Int] = {
if (x forall Character.isDigit) {
x.toInt.successNel
} else {
s"$x is not number".failureNel
}
}
快速失败指令代码:
// fail fast imperative programming
println("---\nFail Fast imperative")
val failFastErrors = mutable.Buffer.empty[String]
if(isAllDigits(a)) {
if(isAllDigits(b)) {
if(isAllDigits(c)) {
val total = a.toInt + b.toInt + c.toInt
println(s"Total = ${total}!!")
} else {
appendError(c, failFastErrors)
}
} else {
appendError(b, failFastErrors)
}
} else {
appendError(a, failFastErrors)
}
if(failFastErrors.nonEmpty) {
println("Errors:")
for(error <- failFastErrors) {
println(error)
}
}
快速失败功能代码(带析取 /):
val resultFunc = for {
x <- toDigitFailFast(a)
y <- toDigitFailFast(b)
z <- toDigitFailFast(c)
} yield (x + y + z)
resultFunc match {
case \/-(total) => println(s"Total = $total")
case -\/(errors) =>
println("Errors:")
errors.foreach(println)
}
快速失败输出(只告诉您第一个错误):
Fail Fast imperative
Errors:
aaa is not number
Fail Fast functional
Errors:
aaa is not number
现在命令式的失败慢速代码:
// fail slow imperative programming
println("---\nFail Slow imperative")
val failSlowErrors = mutable.Buffer.empty[String]
if(!isAllDigits(a)) {
appendError(a, failSlowErrors)
}
if(!isAllDigits(b)) {
appendError(b, failSlowErrors)
}
if(!isAllDigits(c)) {
appendError(c, failSlowErrors)
}
if(failSlowErrors.isEmpty) {
val total = a.toInt + b.toInt + c.toInt
println(s"Total = ${total}!!")
} else {
println("Errors:")
for(error <- failSlowErrors) {
println(error)
}
}
和功能版本(失败慢):
// fail slow functional programming
println("---\nFail Slow functional")
val resultFuncSlow =
(toDigitFS(a) |@| toDigitFS(b) |@| toDigitFS(c)) { _ + _ + _ }
resultFuncSlow match {
case Success(result) => println(result)
case Failure(errors) =>
println("Errors:")
errors.foreach(println)
}
以及两个错误的输出:
Fail Slow imperative
Errors:
aaa is not number
bbb is not number
---
Fail Slow functional
Errors:
aaa is not number
bbb is not number
我正在学习 Scala,今天遇到了使用 Scalaz ValidationNel
的 Fail Slow
机制,但是很难理解如何使用它。我正在阅读这些博客:Blog1 , I am reading this Whosebug post too: Whosebug 但对于非函数式程序员来说真的很难理解。有人可以提供一个简单的例子来说明如何在 Scala 中累积 ValidationNel
中的错误吗?对示例进行描述也会很有帮助。
使用您链接的博客中的示例
val sumV: ValidationNEL[String, Int] = for {
a <- 42.successNel[String]
b <- "Boo".failNel[Int]
c <- "Wah wah".failNel[Int] // by defn of flatMap, can't get here
} yield a + b + c
这是在使用 flatMap 将各种操作链接在一起。例如,42.successNel[String] 创建成功,而 "Boo".failNel[Int] 创建失败。 flatMap 在这里的工作方式是只有在成功时才继续进行下一个操作。所以这是一个 "fail fast" 操作 - 它将第一个失败收集到您的错误案例中并停止。
如果你想 "fail slow" - 即。收集所有可能的故障,您需要使用不同的方法。这就是 Applicative 的用武之地。
val yes = 3.14.successNel[String]
val doh = "Error".failNel[Double]
def addTwo(x: Double, y: Double) = x + y
(yes |@| yes)(addTwo) // Success(6.28)
(doh |@| doh)(addTwo) // Failure(NonEmptyList(Error, Error))
(a |@| b)(someFunctionOfTwoArgsHere)
- 这就是说“执行 'a' 操作,并执行 'b' 操作,如果两者都成功,则执行 someFunctionOfTwoArgsHere(a,b)。否则,采取任何失败并将它们组合。因此,如果 a 失败,但 b 成功,则验证失败,结果为 a 失败。如果 a AND b 失败,则验证失败,结果为 a 和 b 均失败.
上一个答案很好,但我知道你来自 OOP 范式,所以让我再举一个例子来比较这两种范式。
常用代码:
val a = "1"
val b = "aaa"
val c = "bbb"
def isAllDigits(x: String) = x forall Character.isDigit
def appendError(x: String, errors: mutable.Buffer[String]) = errors += s"$x is not number"
type Errors = NonEmptyList[String]
// disjunction \/ for fail fast
def toDigitFailFast(x: String): Errors \/ Int = {
if (isAllDigits(x)) {
x.toInt.right
} else {
s"$x is not number".wrapNel.left
}
}
// validation nel (non empty list) for fail slow
def toDigitFS(x: String): ValidationNel[String, Int] = {
if (x forall Character.isDigit) {
x.toInt.successNel
} else {
s"$x is not number".failureNel
}
}
快速失败指令代码:
// fail fast imperative programming
println("---\nFail Fast imperative")
val failFastErrors = mutable.Buffer.empty[String]
if(isAllDigits(a)) {
if(isAllDigits(b)) {
if(isAllDigits(c)) {
val total = a.toInt + b.toInt + c.toInt
println(s"Total = ${total}!!")
} else {
appendError(c, failFastErrors)
}
} else {
appendError(b, failFastErrors)
}
} else {
appendError(a, failFastErrors)
}
if(failFastErrors.nonEmpty) {
println("Errors:")
for(error <- failFastErrors) {
println(error)
}
}
快速失败功能代码(带析取 /):
val resultFunc = for {
x <- toDigitFailFast(a)
y <- toDigitFailFast(b)
z <- toDigitFailFast(c)
} yield (x + y + z)
resultFunc match {
case \/-(total) => println(s"Total = $total")
case -\/(errors) =>
println("Errors:")
errors.foreach(println)
}
快速失败输出(只告诉您第一个错误):
Fail Fast imperative
Errors:
aaa is not number
Fail Fast functional
Errors:
aaa is not number
现在命令式的失败慢速代码:
// fail slow imperative programming
println("---\nFail Slow imperative")
val failSlowErrors = mutable.Buffer.empty[String]
if(!isAllDigits(a)) {
appendError(a, failSlowErrors)
}
if(!isAllDigits(b)) {
appendError(b, failSlowErrors)
}
if(!isAllDigits(c)) {
appendError(c, failSlowErrors)
}
if(failSlowErrors.isEmpty) {
val total = a.toInt + b.toInt + c.toInt
println(s"Total = ${total}!!")
} else {
println("Errors:")
for(error <- failSlowErrors) {
println(error)
}
}
和功能版本(失败慢):
// fail slow functional programming
println("---\nFail Slow functional")
val resultFuncSlow =
(toDigitFS(a) |@| toDigitFS(b) |@| toDigitFS(c)) { _ + _ + _ }
resultFuncSlow match {
case Success(result) => println(result)
case Failure(errors) =>
println("Errors:")
errors.foreach(println)
}
以及两个错误的输出:
Fail Slow imperative
Errors:
aaa is not number
bbb is not number
---
Fail Slow functional
Errors:
aaa is not number
bbb is not number