comprehension - for scala
Scala Future con filtro para la comprensión. (4)
En su for-comprehension , está filtrando por i == 2 . Debido a que el valor de f1 no es dos, no producirá un Success sino un Failure . El predicado del filtro no se cumple, como lo indica su mensaje de error. Sin embargo, f2.recover devuelve un nuevo Future . El valor de f2 no está manipulado. Todavía almacena el Failure . Esa es la razón por la que recibe el mensaje de error cuando llama a f2.value .
La única alternativa que se me ocurra sería usar otra else en su for-comprehension como se muestra here .
val f2 = for ( i <- f1) yield {
if (i == 2) "Test1"
else "Test2"
}
f2.value
Esto devolverá Some(Success(Test2)) como lo hace su f3.value .
En el siguiente ejemplo, obtengo la excepción java.util.NoSuchElementException: Future.filter predicate is not satisfied
Quiero tener el resultado Future( Test2 ) cuando la verificación if( i == 2 ) falla. ¿Cómo manejo el filtro / si dentro de una comprensión que trata de componer futuros?
A continuación se muestra un ejemplo simplificado que funciona en el REPL de Scala.
Código:
import scala.concurrent.Future
import scala.util.{ Try, Success, Failure }
import scala.concurrent.ExecutionContext.Implicits.global
val f1 = Future( 1 )
val f2 = for {
i <- f1
if( i == 2 )
} yield "Test1"
f2.recover{ case _ => "Test2" }
f2.value
Esta es una solución más idiomática, en mi opinión. Esta función de predicado crea una Future[Unit] o un futuro fallido que contiene su excepción. Para su ejemplo, esto resultaría en un Success("Test1") o un Failure(Exception("Test2")) . Esto es ligeramente diferente de "Test1" y "Test2", pero me parece que esta sintaxis es más útil.
def predicate(condition: Boolean)(fail: Exception): Future[Unit] =
if (condition) Future( () ) else Future.failed(fail)
Lo usas así:
val f2 = for {
i <- f1
_ <- predicate( i == 2 )(new Exception("Test2"))
j <- f3 // f3 will only run if the predicate is true
} yield "Test1"
Me gustó la idea de @pkinsky e hice algunas mejoras. Solté el código para crear un objeto de Exception como este:
val f2 = for {
i <- f1
_ <- shouldTrue( i == 2 )
j <- f3 // f3 will only run if the predicate is true
} yield "Test1"
shouldTrue función shouldTrue se implementa utilizando la biblioteca de sourcecode lihaoyi:
def shouldTrue(condition: sourcecode.Text[Boolean])(implicit enclosing: sourcecode.Enclosing, file: sourcecode.File, line: sourcecode.Line): Future[Unit] =
if (condition.value) Future.successful( () ) else Future.failed(new Exception(s"${condition.source} returns false/n/tat ${file.value}:${line.value}"))
Entonces genera automáticamente un mensaje de excepción más significativo:
java.lang.Exception: i == 2 returns false
at /path/to/example.scala:17
Por supuesto que yo mismo descubrí una solución. ¿Acaso hay soluciones mejores, más idiomáticas?
import scala.concurrent.Future
import scala.util.{ Try, Success, Failure }
import scala.concurrent.ExecutionContext.Implicits.global
val f1 = Future( 1 )
val f2 = for {
i <- f1
if( i == 2 )
} yield "Test1"
val f3 = f2.recover{ case _ => "Test2" }
// OR val f3 = f2.fallbackTo( Future( "Test2" ) )
f3.value