2015-07-20 15 views
5

Im folgenden AusschnittScala Zukunft [Option [T]] Un Verpackung

trait MyType1; trait MyType2 
import scala.concurrent.Promise 

val p1 = Promise[Option[MyType1]]() 
val p2 = Promise[MyType2]() 

Ich gehe in P1 und P2 an einem anderen Funktion, wo ich das Versprechen mit einer erfolgreichen Zukunft abzuschließen. Nach dem Aufruf dieser Funktion, ich versuche, den Wert in dem Versprechen zu lesen:

trait Test { 
    // get the Future from the promise 
    val f1 = p1.future 
    val f2 = p2.future 

    for { 
    someF1Elem <- f1 
    f1Elem  <- someF1Elem 
    f2Elem  <- f1Elem 
    } yield { 
    // do something with f1Elem and f2Elem 
    "..." 
    } 
} 

Wenn ich versuche, dies zu kompilieren, ich einige Compiler Probleme bekommen.

Error:(52, 19) type mismatch; 
found : Option[Nothing] 
required: scala.concurrent.Future[?] 
     flElem  <- someF1Elem 
       ^

IntelliJ zeigt keine Fehler oder was auch immer und die Typen sehen ausgerichtet aus. Aber ich bin mir nicht sicher, warum der Compiler unglücklich ist! Irgendwelche Hinweise?

Antwort

15

Ihre Verstehenstypen müssen konsistent sein, so dass Sie Option und Future nicht so mischen können, wie Sie es tun.

In Ihrem Beispiel f1 gibt ein Future[Option[MyType1]] während f2 kehrt ein Future[MyType2]

Denken Sie daran, dass ein für das Verständnis desugars zu einer Reihe von flatMap/map und potenziell withFilter.

Auch die (vereinfacht) Unterschriften von flatMap für Future[A] und Option[A] sind

def flatMap[B](f: A => Future[B]): Future[B] 
def flatMap[B](f: A => Option[B]): Option[B] 

Die ersten beiden Schritte des für Verständnis desugar zu

f1.flatMap { someF1Elem => 
    someF1Elem.flatMap { f1Elem => // this returns a `Option[MyType1]` 
    ... 
    } 
} 

sehen nun die Fehler?


Nun, um dies zu beheben gibt es ein paar Ansätze, denen Sie folgen können. Sehr praktisch ist die Verwendung von Monad Transformers, mit denen Sie zum Beispiel Future und Option zu einem einzigen Monad-Typ OptionT kombinieren können.

Insbesondere können Sie hin und her von Future[Option[A]] zu OptionT[Future, A] gehen. Die Grundidee ist, dass Sie FlatMap auf den letzteren und einen Wert vom Typ A extrahieren können.

Aber vorher müssen Sie Ihre Typen der „richtige Form“ machen, so dass beide ein sind Future[Option[Something]]

Hier ist ein Beispiel unter Verwendung von scalaz

import scalaz._; import Scalaz._ ; import scalaz.OptionT._ 
import scala.concurrent.{ Promise, Future } 
import scala.concurrent.ExecutionContext.Implicits.global 

trait MyType1 
trait MyType2 

val p1 = Promise[Option[MyType1]]() 
val p2 = Promise[MyType2]() 

val f1 = p1.future 
val f2 = p2.future 

val res = for { 
    f1Elem <- optionT(f1) 
    f2Elem <- f2.liftM[OptionT] 
} yield { 
    println(s"$f1Elem $f2Elem") 
} 

optionT eine OptionT[Future, A] ein Future[Option[A]] gegeben baut , während liftM[OptionT] dasselbe erreicht, wenn Sie eine Future[A] haben

Der Typ von res ist OptionT[Future, Unit] in diesem Fall, und Sie können eine Future[Option[Unit]] erhalten, indem Sie run darauf aufrufen.

2

Zum Verständnis müssen die Ausdrücke auf der rechten Seite der <- kompatible Typen haben. Das ist, weil für die Übersichten im Grunde genommen syntaktischer Zucker für verschachtelte flatMap Aufrufe ist. Um dieses Problem zu lösen, können Sie entweder ein Verständnis für das Verständnis in einem anderen für das Verständnis geschachtelt haben oder Sie können Methoden wie get oder map auf die Option s verwenden.

Eine andere Möglichkeit ist die Verwendung von monad transformers, aber das würde den Rahmen dieser Antwort sprengen.

4

bei "How does yield work" Sehen, Ihr für das Verständnis entspricht

f1.flatMap(someF1Elem: Option[MyType1] => 
    someF1Elem.flatMap(f1Elem => 
    f1Elem.map(f2Elem => 
     "..." 
    ) 
) 
) 

Im Grunde, was passiert, ist dies: flatMap auf einem Future definiert eine Funktion zu nehmen, die eine andere Future zurückgibt. Dies gibt Ihnen einen ähnlichen Fehler:

<console>:64: error: value map is not a member of MyType1 f1Elem.map(f2Elem => ^ <console>:63: error: type mismatch; found : Option[Nothing] required: scala.concurrent.Future[?] someF1Elem.flatMap(f1Elem => ^

Wenn Sie die Operation wollen asynchron auszuführen, das heißt wirklich die Future Instanzen abbildet, werden Sie zu einem Future[Option[X]] bekommen müssen zurück. Als Kim vorgeschlagen, können Sie nisten das Verständnis:

import scala.concurrent.{Future, Promise} 

def test: Future[Option[String]] = { 
    val p1 = Promise[Option[MyType1]]() 
    val p2 = Promise[MyType2]() 
    // ... run code that completes the promises 

    val f1 = p1.future 
    val f2 = p2.future 

    for { 
    someF1Elem: Option[MyType1] <- f1 
    f2Elem  <- f2 
    } yield { 
    for (f1Elem <- someF1Elem) yield "something" 
    } 
} 

Sie könnten auch die resultierende Future machen fehlschlagen, wenn die Option nicht definiert ist (NoSuchElementException)

def test: Future[String] = { 
    val p1 = Promise[Option[MyType1]]() 
    val p2 = Promise[MyType2]() 
    // ... run code that completes the promises 

    val f1 = p1.future 
    val f2 = p2.future 

    for { 
    someF1Elem: Option[MyType1] <- f1 
    f2Elem <- f2 
    } yield { 
    val f1Elem = someF1Elem.get // may cause an exception and fail the future 
    "something" 
    } 
}