2015-02-05 5 views
7

Ich habe viele Male über Scala-Futures gelesen, Callback-Probleme zu reduzieren. Ich habe einen Code, der problematisch aussieht.Scala Futures Callback-Hölle

val a = Future(Option(Future(Option(10)))) 

a.map { b => 
    b.map { c => 
    c.map { d => 
     d.map { res => 
     res + 10 
     } 
    } 
    } 
} 

Wie kann ich diesen Code flacher machen?

// bearbeiten @againstmethod

for{ 
    b <- a 
    c <- b 
    d <- c 
    res <- d 
} yield res + 10 

Dieser Code

Error:(21, 8) type mismatch; found : Option[Int] required:
scala.concurrent.Future[?] res <- d
^

+4

, wenn Sie mit einem Beispiel kommen die tatsächlich kompiliert, wenn man sich in eine IDE Pasten, könnte es einfacher seine Verbesserung vorzuschlagen ... –

+0

@KimStebel veränderte einfach den Code so ist es universeller –

+0

Sie sollten vermeiden, Future's an erster Stelle zu stapeln, indem Sie 'Future.traverse' oder' Future.sequence' verwenden. – Dimitri

Antwort

1

Eigentlich war die Antwort ziemlich direkt.

for { 
a <- b 
c <- a.get 
} yield c.get + 10 

Erscheint genug zu sein, denn wenn x.get + 10 ausfällt (wegen None + 10) die Zukunft nicht nur. So funktioniert es immer noch einen einfachen Rückfall verwenden

val f = for { 
a <- b 
c <- a.get 
} yield c.get + 10 
f fallbackTo Future.successful(0) 
2

nicht kompilieren können Sie einen for comprehension verwenden. Im Beispiel:

import scala.concurrent.Future 
import scala.concurrent.ExecutionContext.Implicits.global 

object Stuff extends App { 
    val result = for { 
    f1 <- Future { 10 + 1 } 
    f2 <- Future { f1 + 2 } 
    } yield f2 
    result.onComplete(println) 
} 

Wo Ergebnis wird 13.

Jede Klasse, die eine richtige map und flatMap Funktion implementiert, kann in einem for auf diese Weise verwendet werden.

Wenn Sie keine andere Abhängigkeit dagegen haben, können Sie auch eine Bibliothek wie scalaz verwenden und explizit monadischen Bindung verwenden, um Dinge zu abflachen (EDIT einige Option Typen codiert einen Kommentar unten zu adressieren):

import scalaz._ 
import Scalaz._ 
import scala.concurrent._ 
import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.duration._ 
import scala.util.{Success,Failure} 

object BindEx extends App { 

    def f1(i: String): Future[Int] = Future { i.length } 
    def f2(i: Int): Future[Option[Double]] = Future { Some(i/Math.PI) } 
    def f3(i: Option[Double]): Future[Option[Double]] = Future { 
    i match { 
     case Some(v) => Some(Math.round(v)) 
     case _ => None 
    } 
    } 

    val result = 
    Monad[Future].point("Starting Point") >>= 
    f1 >>= 
    f2 >>= 
    f3 

    result.onComplete { x => 
    x match { 
     case Success(value) => println("Success " + value) 
     case Failure(ex) => println(ex) 
    } 
    } 

    Await.result(result, 1 seconds) 
} 

und schließlich, wenn Sie nur parallele Operationen, die Sie binden möchten schließlich gelungen ist, die unabhängig sind, können Sie scalaz applicative Builder verwenden:

val result = (
    Future { 10 + 10 } |@| 
    Future { 20 - 3 } |@| 
    Future { Math.PI * 15 } 
) { _ + _/_} 
    println(Await.result(result, 1 seconds)) 

Damit können alle drei Futures abgeschlossen ist, dann Block gelten für die 3 Argumente.

+0

Ja, es ist wahr. Aber zum Verständnis gibt nichts über das Scheitern zurück, richtig? –

+0

Korrigieren und wenn Sie sie in einer solchen Zeile erstellen, werden sie seriell ausgeführt. Es ist nur ein weiteres Werkzeug, das helfen kann. –

+0

'Ergebnis' ist eine' Zukunft' in der Weise, die Sinn macht; Sie können 'result.onFailure' oder ähnliches verwenden, um Fehler zu behandeln. Sie verschwinden nicht einfach still. – lmm

0

Ich habe sie noch nicht benutzt, aber sie sollten genau das sein, wonach Sie suchen: Monad Transformatoren.

Im Grunde nimmt ein Monadetransformator eine Monade (wie Future) und fügt Funktionalität wie die von Option zur Verfügung gestellte Funktionalität hinzu und gibt eine umgewandelte Monade zurück. Ich denke, es gibt sogar einen Option-Monade-Transformator in Scalaz. Dies sollte es Ihnen ermöglichen, geschachtelte Optionen in Futures zu verwenden und dennoch eine flache Codestruktur zu verwenden, die für Comprehensions verwendet wird.

Für einige Beispiele siehe http://blog.garillot.net/post/91731853561/a-question-about-the-option-monad-transformer und https://softwarecorner.wordpress.com/2013/12/06/scalaz-optiont-monad-transformer/.

Verwandte Themen