【问题标题】:Scalaz fail slow using ValidationNelScalaz 使用 ValidationNel 失败缓慢
【发布时间】:2017-10-03 21:26:39
【问题描述】:

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

【问题讨论】:

    标签: scala scalaz


    【解决方案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,“Boo”.failNel[Int] 创建一个失败。 flatMap 在这里的工作方式是只有在成功时才能继续进行下一个操作。所以这是一个“快速失败”操作——它将第一个失败收集到你的错误案例中并停止。

    如果您想“缓慢失败” - 即。收集所有可能的故障,您需要使用不同的方法。这就是 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 都失败。

    【讨论】:

    • 感谢您的回答,val yes = 3.14.successNel[String] 的声明究竟是什么意思?我来自 OOPs 背景,我正在尝试将您的示例与 if(condition) 声明相关联。
    • val yes = 3.14.successNel[String] 正在创建 ValidationNEL[String, Int] 对象的实例。类型参数(String 和 Int)分别指的是失败和成功的情况。这种对象要么包含电梯类型的非空列表(在这种情况下为字符串),要么包含右侧类型的单个实例(Int)。这种类型的对象具有编码到其中的|@| 行为,并且该行为是 - 获取另一个 ValidationNel,并检查对象的实际值是什么 - 它们都是成功的吗?
    • 在这种情况下,获取正确的参数并通过函数运行它,并将结果放入 Success 对象中。或者它们都是失败的,还是其中之一是失败的?在这种情况下,获取失败并将它们组合在一个列表中,然后将该列表放入一个失败对象中,
    【解决方案2】:

    以前的答案很好,但我知道您来自 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 命令的代码:

    // 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-05
      • 2015-10-25
      • 1970-01-01
      相关资源
      最近更新 更多