2013-07-25 10 views
21

W poniższym przykładzie uzyskać wyjątkiem java.util.NoSuchElementException: Future.filter predicate is not satisfiedScala Future z filtrem dla zrozumienia

chcę mieć wynik Future(Test2) gdy kontrola if(i == 2) zawiedzie. Jak mogę obsłużyć filtr/czy w ramach rozumienia, które zajmuje się komponowaniem kontraktów terminowych?

Poniżej znajduje się uproszczony przykład, który działa w REPL Scala.

Kod:

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 

Odpowiedz

11

W swojej for-comprehension, jesteś filtrując przez i == 2. Ponieważ wartość f1 nie jest równa dwóm, nie da się uzyskać Success, ale zamiast tego Failure. Predykat filtra nie jest spełniony, tak jak informuje Cię komunikat o błędzie. Jednak f2.recover zwraca nową Future. Wartość f2 nie jest modyfikowana. Wciąż przechowuje Failure. To jest powód, dla którego pojawi się komunikat o błędzie, gdy zadzwonisz pod numer f2.value.
Jedyną alternatywą, o której mogę pomyśleć, byłoby użycie else w twoim for-comprehension, jak pokazano here.

val f2 = for (i <- f1) yield { 
    if (i == 2) "Test1" 
    else "Test2" 
} 
f2.value 

ten powróci Some(Success(Test2)) jako swój f3.value robi.

+0

Jednak jeśli f2 nie powiedzie się z powodu błędu "i <- f1", wynik nie będzie "Test2", ale nadal będzie to Niepowodzenie. –

8

Oczywiście, że zorientowali się jedno rozwiązanie siebie. Być może istnieją lepsze, bardziej idiomatyczne rozwiązania?

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 
+1

Wspomniano w docs, a także: http://docs.scala-lang.org/overviews/core/futures.html#functional_composition_and_forcomprehensions nie kiedy przyjrzę drugie spojrzenie. – Magnus

+1

Myślę, że użycie 'odzyskać' tutaj jest w porządku. Jeśli chcesz, aby odzyskiwanie było stosowane tylko do wyjątku opartego na predykacie filtru, powinieneś zmienić "case _" na 'case ex: NoSuchElementException', gdzie' NoSuchElementException' pochodzi z 'java.util'. – cmbaxter

+0

Jürgen: To jest sześciomiesięczne pytanie, powyższy kod był przykładem wyjętym z sesji REPL, więc jest nastawiony na klarowność. Komentujesz, że możesz umieszczać nawiasy wokół instrukcji w Scali? Nie widząc, jaki jest twój wkład. – Magnus

27

Jest to bardziej idiomatyczne rozwiązanie, moim zdaniem. Ta funkcja predykatu tworzy albo Future[Unit] lub nieudaną przyszłość zawierającą wyjątek. Na przykład, spowoduje to albo Success("Test1") lub Failure(Exception("Test2")). To trochę różni się od "Test1" i "Test2", ale uważam tę składnię za bardziej użyteczną.

def predicate(condition: Boolean)(fail: Exception): Future[Unit] = 
    if (condition) Future(()) else Future.failed(fail) 

go używać tak:

val f2 = for { 
    i <- f1 
    _ <- predicate(i == 2)(new Exception("Test2")) 
    j <- f3 // f3 will only run if the predicate is true 
} yield "Test1" 
+1

To była bardzo przydatna rada. Dzięki! – Tommi

+0

Znacznie wyraźniejsze, dzięki! – Artemis