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