Si no hubiera tenido que devolver un mensaje diferente para el caso None, este sería un caso de uso ideal para para la comprensión. En su caso, es probable que desee utilizar la mónada de Validación, como la que puede encontrar en Scalaz. Ejemplo (http://scalaz.github.com/scalaz/scalaz-2.9.0-1-6.0/doc.sxr/scalaz/Validation.scala.html).
En la programación funcional, no debe arrojar excepciones sino dejar que las funciones que pueden fallar devuelvan un O [A, B], donde por convención A es el tipo de resultado en caso de falla y B es el tipo de resultado en caso de éxito Puede hacer coincidir la izquierda (a) o la derecha (b) para manejar, respectivamente, los dos casos.
Puede pensar en la mónada de validación como una extendida O bien [A, B] donde la aplicación de funciones posteriores a una validación arrojará un resultado, o la primera falla en la cadena de ejecución.
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]
Su código ahora se puede refactorizar usando la mónada de Validación en tres capas de validación. Debe reemplazar básicamente su mapa con una validación como la siguiente:
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")
)))
}
}
}
Y luego los de cadena como la siguiente featureValidation(jsonValidation(request))
muchas gracias por su respuesta, el código que publiqué SE compila, pero creo que es un poco demasiado complicado para este ejemplo ... – opensas
¿Y cómo se mantienen las diferentes respuestas de 'BadRequest' en los diferentes niveles? Esa parece ser la cuestión clave que impide el enfoque directo. Me pregunto si la coincidencia de patrones depende de esto. –
Eso es correcto. En un lenguaje imperativo, simplemente fallaría en el método tan pronto como encuentre un error. Pero cuando traté de hacerlo, me enfrenté a varios problemas con la declaración de devolución http://stackoverflow.com/questions/11929485/scala-problems-with-return-statement/11929616#11929616 – opensas