Scalaz使用ValidationNel失败缓慢

时间:2017-10-03 21:26:39

标签: scala scalaz

我正在学习Scala,今天使用Fail Slow遇到Scalaz ValidationNel机制但是很难理解如何使用它。我正在阅读这些博客:Blog1,我也在阅读这篇StackOverflow文章:StackOverflow但是对于非功能性程序员来说真的很难理解。有人可以提供一个关于如何在Scala中ValidationNel累积错误的简单示例吗?对这个例子进行描述也很有帮助。

2 个答案:

答案 0 :(得分:1)

使用您已关联的博客中的示例

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]创建一个Success,&#34; Boo&#34; .failNel [Int]创建一个失败。 flatMap在这里工作的方式是仅在成功时继续下一步操作。所以这是一个&#34;快速失败&#34;操作 - 它将第一次失败收集到您的错误案例中并停止。

如果你想&#34;失败慢&#34; - 即。收集所有可能的故障,您需要使用不同的方法。这就是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) - 这说的是&#34;执行&#39; a&#39;操作,并执行&#39;&#39;操作,如果两者都成功,执行someFunctionOfTwoArgsHere(a,b)。否则,采取任何失败并将它们结合起来。因此,如果a失败,但b成功,则会因失败而导致验证失败。如果AND b失败,则会导致验证失败,并且a和b的结果都会失败。

答案 1 :(得分:0)

先前的答案很好,但我了解您来自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