[英]scala: idiomatic and functional way to add errors to a list of errors
我有以下验证方法:
def validate(wine: Wine): List[Error] = {
var errors = List[Error]()
if (Validate.isEmptyWord(wine.name)) {
errors ::= ValidationError("name", "Name not specified")
} else {
if (isDuplicate(wine, "name")) {
errors ::= ValidationError("name", "There already exists a wine with the name '%s'".format(wine.name))
}
}
if (Validate.isEmptyWord(wine.grapes)) {
errors ::= ValidationError("grapes", "Grapes not specified")
}
if (Validate.isEmptyWord(wine.country)) {
errors ::= ValidationError("country", "Country not specified")
}
// more stuff like this and finnally
errors.reverse
}
你明白了
你将如何修改它以避免var List [Error]并使其更具功能性?
Scalaz提供了一个Validation
类,可以很容易地解决这类问题。 这个Stack Overflow答案中有一个更详细的如何使用Validation
例子,但我也会在这里给出一个草图,说明它在你的情况下是如何工作的。 我将假设以下设置:
case class Wine(name: String, grapes: String, country: String)
case class ValidationError(name: String, msg: String)
现在我们可以编写几种验证方法(注意我使用的是Scalaz 7):
import scalaz._, Scalaz._
def checkNonempty(v: String, name: String, msg: String) =
if (v.nonEmpty) v.successNel else ValidationError(name, msg).failNel
def checkDuplicate(v: String, name: String, msg: String) =
if (true) v.successNel else ValidationError(name, msg).failNel
当然,您应该在最后一行添加自己的复制检查。 然后我们可以将它们包装在一起:
def createWine(name: String, grape: String, country: String) = (
checkNonempty(name, "name", "Name not specified").flatMap(_ =>
checkDuplicate(name, "name",
"There already exists a wine with the name '%s'".format(name)
)
) |@|
checkNonempty(grape, "grape", "Grape not specified") |@|
checkNonempty(country, "country", "Country not specified")
)(Wine.apply)
现在,如果我们写这样的东西:
val result: ValidationNEL[ValidationError, Wine] = createWine(
"Whatever Estates", "Whatever Grape", "U.S."
)
我们将获得成功价值:
Success(Wine(Whatever Estates,Whatever Grape,U.S.))
但是如果我们给它无效的输入:
val result: ValidationNEL[ValidationError, Wine] = createWine(
"", "Some Grape", ""
)
我们将获得累积错误的列表:
Failure(
NonEmptyList(
ValidationError(name,Name not specified),
ValidationError(country,Country not specified)
)
)
当然,你也可以推出自己的验证逻辑,但是如果你做了很多这样的事情,使用像Scalaz这样的库可能是值得的。
编写isX
方法以返回Option[Error]
而不是Boolean
,然后
List(
Validate.isEmptyWord(wine.name, "name", "Name not specified").
orElse( Validate.isDuplicate(wine.name, "name", "There already exists...") ),
Validate.isEmptyWord(wine.grapes, "grapes", "Grapes not specified"),
...
).flatten
你会有错误的。 此外,您还可以删除大量其他重复项(例如,如果它始终为"x", "X not specified"
,则可以提供"x"
并将其余部分填入isEmptyWord
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.