如何在 Scala 中实现简单的验证
How to implement simple validation in Scala
假设我需要验证请求参数。验证结果是 Success
或 Failure
和 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)
}
}
现在,如果我使用函数 checkA
、checkB
和 checkC
验证三个参数,我可以轻松地将它们组成如下:
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 库中实现的吗?
它确实是一个Monoid
,你可以更精确:它是一个List[String]
(直到同构)。 ValidationResult
确实同构于 List[String]
,Success
对应 Nil
,而 andAlso
是 :::
/ ++
的串联。
这是有道理的,一个ValidationResult
就是一个错误列表,当有none的时候,就代表成功了。
但是,正如您在一开始就注意到的,这都相当于使用 ValidationNel[String, Unit]
,其中 Unit
、"no data of interest" 是有趣的部分。如果意味着您将单独处理实际数据。你可能在这里赢了一点点,那就是避免使用 Applicative
的语法,在你的代码中加入 |@|
之类的东西;此外,Monads and Co 的一个不常提及的价格,使得使用调试器更容易。但是有一个缺点,随着代码的增长,可能会出现错误的地方也会成倍增加,手动管理流程很快就会变得很痛苦,我不会那样做。
通常的替代方法是例外。
假设我需要验证请求参数。验证结果是 Success
或 Failure
和 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)
}
}
现在,如果我使用函数 checkA
、checkB
和 checkC
验证三个参数,我可以轻松地将它们组成如下:
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 库中实现的吗?
它确实是一个Monoid
,你可以更精确:它是一个List[String]
(直到同构)。 ValidationResult
确实同构于 List[String]
,Success
对应 Nil
,而 andAlso
是 :::
/ ++
的串联。
这是有道理的,一个ValidationResult
就是一个错误列表,当有none的时候,就代表成功了。
但是,正如您在一开始就注意到的,这都相当于使用 ValidationNel[String, Unit]
,其中 Unit
、"no data of interest" 是有趣的部分。如果意味着您将单独处理实际数据。你可能在这里赢了一点点,那就是避免使用 Applicative
的语法,在你的代码中加入 |@|
之类的东西;此外,Monads and Co 的一个不常提及的价格,使得使用调试器更容易。但是有一个缺点,随着代码的增长,可能会出现错误的地方也会成倍增加,手动管理流程很快就会变得很痛苦,我不会那样做。
通常的替代方法是例外。