在验证几个连续条件时,我经常会遇到很多嵌套的.map和.getOrElse
例如:
def save() = CORSAction { request =>
request.body.asJson.map { json =>
json.asOpt[Feature].map { feature =>
MaxEntitiyValidator.checkMaxEntitiesFeature(feature).map { rs =>
feature.save.map { feature =>
Ok(toJson(feature.update).toString)
}.getOrElse {
BadRequest(toJson(
Error(status = BAD_REQUEST, message = "Error creating feature entity")
))
}
}.getOrElse {
BadRequest(toJson(
Error(status = BAD_REQUEST, message = "You have already reached the limit of feature.")
))
}
}.getOrElse {
BadRequest(toJson(
Error(status = BAD_REQUEST, message = "Invalid feature entity")
))
}
}.getOrElse {
BadRequest(toJson(
Error(status = BAD_REQUEST, message = "Expecting JSON data")
))
}
}
Run Code Online (Sandbox Code Playgroud)
你明白了
我只想知道是否有一些惯用的方法可以让它更清晰
Edm*_*984 12
如果你没有不得不返回不同的消息为无的情况下,这将是一个理想的使用情况进行了理解.在您的情况下,您可能希望使用Validation monad,就像您可以在Scalaz中找到的那样.示例(http://scalaz.github.com/scalaz/scalaz-2.9.0-1-6.0/doc.sxr/scalaz/Validation.scala.html).
在函数式编程中,不应该抛出异常,而是让失败的函数返回Either [A,B],其中约定A是失败时的结果类型,B是成功时的结果类型.然后,您可以匹配左(a)或右(b)来分别处理这两种情况.
您可以将Validation monad视为扩展的Either [A,B],其中将后续函数应用于验证将产生结果,或者执行链中的第一个失败.
sealed trait Validation[+E, +A] {
import Scalaz._
def map[B](f: A => B): Validation[E, B] = this match {
case Success(a) => Success(f(a))
case Failure(e) => Failure(e)
}
def foreach[U](f: A => U): Unit = this match {
case Success(a) => f(a)
case Failure(e) =>
}
def flatMap[EE >: E, B](f: A => Validation[EE, B]): Validation[EE, B] = this match {
case Success(a) => f(a)
case Failure(e) => Failure(e)
}
def either : Either[E, A] = this match {
case Success(a) => Right(a)
case Failure(e) => Left(e)
}
def isSuccess : Boolean = this match {
case Success(_) => true
case Failure(_) => false
}
def isFailure : Boolean = !isSuccess
def toOption : Option[A] = this match {
case Success(a) => Some(a)
case Failure(_) => None
}
}
final case class Success[E, A](a: A) extends Validation[E, A]
final case class Failure[E, A](e: E) extends Validation[E, A]
Run Code Online (Sandbox Code Playgroud)
现在可以通过将Validation monad用于三个验证层来重构您的代码.您应该基本上使用以下验证替换您的地图:
def jsonValidation(request:Request):Validation[BadRequest,String] = request.asJson match {
case None => Failure(BadRequest(toJson(
Error(status = BAD_REQUEST, message = "Expecting JSON data")
)
case Some(data) => Success(data)
}
def featureValidation(validatedJson:Validation[BadRequest,String]): Validation[BadRequest,Feature] = {
validatedJson.flatMap {
json=> json.asOpt[Feature] match {
case Some(feature)=> Success(feature)
case None => Failure( BadRequest(toJson(
Error(status = BAD_REQUEST, message = "Invalid feature entity")
)))
}
}
Run Code Online (Sandbox Code Playgroud)
}
然后你将它们链接起来如下 featureValidation(jsonValidation(request))
这是使用 monad 可以清理代码的经典示例。例如,您可以使用 Lift's ,它不以任何方式Box绑定。Lift那么你的代码将如下所示:
requestBox.flatMap(asJSON).flatMap(asFeature).flatMap(doSomethingWithFeature)
Run Code Online (Sandbox Code Playgroud)
其中asJson是从请求到 a 的函数Box[JSON],是从 a到其他某个的asFeature函数。该框可以包含一个值,在这种情况下,flatMap 使用该值调用函数,或者它可以是 的实例,在这种情况下,不会调用传递给它的函数。FeatureBoxFailureflatMap
如果您发布了一些可以编译的示例代码,我可以发布一个可以编译的答案。