如何在Scala中实现简单验证

时间:2015-03-08 09:36:59

标签: validation scala scalaz

假设我需要验证请求参数。验证结果为SuccessFailure NonEmptyList[String]。我可以使用ValidationNel[String, Unit],但似乎有点矫枉过正。我想我需要一个更简单的抽象(见下文)。

trait ValidationResult
object Success extends ValidationResult
class Failure(errors: NonEmptyList[String]) extends ValidationResult

和二进制操作andAlso结合两个结果:

trait ValidationResult {
  def andAlso(other: ValidationResult): ValidationResult = 
    (this, other) match {
      case (Success, Success) => Success
      case (Success, failure @ Failure(_)) => failure
      case (failure @ Failure(_), Success) => failure
      case (Failure(errors1), Failure(errors2)) => Failure(errors1 + errors2) 
    } 
}

现在,如果我使用函数checkAcheckBcheckC验证三个参数,我可以轻松地将它们组成如下:

def checkA(a: A): ValidationResult = ...
def checkB(b: B): ValidationResult = ...
def checkC(c: C): ValidationResult = ...
def checkABC(a: A, b: B, c: C) = checkA(a) andAlso checkB(b) andAlso checkC(c)

有意义吗?
这个抽象有名字吗?也许是Monoid
它是在scalaz还是其他任何scala库中实现的?

1 个答案:

答案 0 :(得分:3)

确实是Monoid,你可以更精确:它是List[String](直到同态)。 ValidationResult确实与List[String]同构,SuccessNilandAlso为连结::: / ++

这是有道理的,ValidationResult是一个错误列表,当没有错误时,这意味着成功。

但是,正如您在开头注意到的那样,这一切都等于使用ValidationNel[String, Unit],其中Unit,"没有感兴趣的数据"是有趣的部分。如果意味着您将单独处理实际数据。你可能会在这里赢得一点点,而这一点就是避免使用Applicative的语法,用|@|等代码填充你的代码;另外,一个不常提到的Monads和Co的价格,使得使用调试器更容易。但是有一个缺点,因为你的代码随着错误可能发生的地方增长而增加,手动管理流程将很快变得痛苦,我不会这样做。

通常的替代方案是例外。