gpt4 book ai didi

scala:如何以功能方式处理验证

转载 作者:行者123 更新时间:2023-12-04 09:41:28 24 4
gpt4 key购买 nike

我正在开发一种方法,如果它传递了条件列表,则该方法应该保留一个对象。

如果任何(或多个)条件失败(或出现任何其他类型的错误),则应返回包含错误的列表,如果一切顺利,则应返回已保存的实体。

我在想这样的事情(当然是伪代码):

request.body.asJson.map { json =>
json.asOpt[Wine].map { wine =>
wine.save.map { wine =>
Ok(toJson(wine.update).toString)
}.getOrElse { errors => BadRequest(toJson(errors))}
}.getOrElse { BadRequest(toJson(Error("Invalid Wine entity")))}
}.getOrElse { BadRequest(toJson(Error("Expecting JSON data")))}

也就是说,我想把它当作一个 Option[T],如果任何验证失败,而不是返回 None它给了我错误列表......

这个想法是返回一组 JSON 错误......

所以问题是,这是处理这种情况的正确方法吗?在 Scala 中完成它的方法是什么?

——

哎呀,刚刚发布了问题并发现了Either

http://www.scala-lang.org/api/current/scala/Either.html

无论如何,我想知道您对所选方法的看法,以及是否有其他更好的替代方法来处理它。

最佳答案

使用 scalaz 你有 Validation[E, A] ,就像 Either[E, A]但具有如果 E 的属性是一个半群(意思是可以连接的东西,比如列表),而不是多个验证结果可以以保留所有发生的错误的方式组合。

例如,使用 Scala 2.10-M6 和 Scalaz 7.0.0-M2,其中 Scalaz 有一个自定义 Either[L, R]命名 \/[L, R]默认情况下是右偏的:

import scalaz._, Scalaz._

implicit class EitherPimp[E, A](val e: E \/ A) extends AnyVal {
def vnel: ValidationNEL[E, A] = e.validation.toValidationNEL
}

def parseInt(userInput: String): Throwable \/ Int = ???
def fetchTemperature: Throwable \/ Int = ???
def fetchTweets(count: Int): Throwable \/ List[String] = ???

val res = (fetchTemperature.vnel |@| fetchTweets(5).vnel) { case (temp, tweets) =>
s"In $temp degrees people tweet ${tweets.size}"
}

这里 resultValidation[NonEmptyList[Throwable], String] ,包含所有发生的错误(温度传感器错误和/或推特错误或无)或成功消息。然后您可以切换回 \/为了方便。

注意:Either 和Validation 的区别主要在于Validation 可以累积错误,但不能 flatMap丢失累积的错误,而使用或者你不能(轻松)累积但可以 flatMap (或在理解中)并且可能会丢失除第一条错误消息之外的所有内容。

关于错误层次结构

我想这可能对你感兴趣。不管使用scalaz/ Either/ \// Validation ,我体验到入门很容易,但继续前进需要一些额外的工作。问题是,您如何以有意义的方式从多个出错的函数中收集错误?当然,您可以使用 ThrowableList[String]无处不在,享受轻松的时光,但听起来不太实用或可解释。想象一下,得到一个错误列表,例如“缺少 child 年龄”::“IO 错误读取文件”::“除以零”。

所以我的选择是创建错误层次结构(使用 ADT-s),就像将 Java 的已检查异常包装到层次结构中一样。例如:
object errors {

object gamestart {
sealed trait Error
case class ResourceError(e: errors.resource.Error) extends Error
case class WordSourceError(e: errors.wordsource.Error) extends Error
}

object resource {
case class Error(e: GdxRuntimeException)
}

object wordsource {
case class Error(e: /*Ugly*/ Any)
}

}

然后,当使用具有不同错误类型的错误函数的结果时,我将它们加入到相关的父错误类型下。
for {
wordSource <-
errors.gamestart.WordSourceError <-:
errors.wordsource.Error <-:
wordSourceCreator.doCreateWordSource(mtRandom).catchLeft.unsafePerformIO.toEither

resources <-
errors.gamestart.ResourceError <-:
GameViewResources(layout)

} yield ...

这里 f <-: e映射函数 fe: \/ 的左边自 \/是一个二仿函数。对于 se: scala.Either你可能有 se.left.map(f) .

这可以通过提供 shapeless 进一步改进 HListIso s 能够绘制漂亮的错误树。

修订

更新: (e: \/).vnel将故障侧提升为 NonEmptyList所以如果我们有一个失败,我们至少有一个错误(是:或没有)。

关于scala:如何以功能方式处理验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12067296/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com