2015-07-20 19 views
5

w poniższym przykładzie,Scala przyszłości [opcja [t]] Un pakowania

trait MyType1; trait MyType2 
import scala.concurrent.Promise 

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

przekazać P1 i P2 na inne stanowisko, gdzie zakończenia Promise pomocą skutecznej przyszłości. Po wywołaniu tej funkcji, próbuję odczytać wartość w obietnicy:

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 
    "..." 
    } 
} 

Kiedy próbuję skompilować ten, mam pewne problemy kompilatora.

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

IntelliJ nie pokazuje błędów ani tego, co-tak-kiedykolwiek, a typy wyglądają równo. Ale nie jestem pewien, dlaczego kompilator jest nieszczęśliwy! Jakieś wskazówki?

Odpowiedz

15

Twoje typy do zrozumienia muszą być spójne, więc nie możesz swobodnie mieszać Option i Future w taki sposób, w jaki robisz.

W przykładzie f1 Zwraca Future[Option[MyType1]] while f2 Zwraca Future[MyType2]

pamiętać, że dla zrozumienia desugars do serii flatMap/map i potencjalnie withFilter.

Także (uproszczony) podpisy flatMap dla Future[A] i Option[A]

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

Pierwsze dwa kroki do-zrozumienia desugar do

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

zobaczyć błąd teraz?


Teraz, aby rozwiązać ten problem, można zastosować kilka metod. Bardzo przydatne jest użycie transformatorów Monad, które umożliwiają łączenie (na przykład) Future i Option w pojedynczy monad typu OptionT.

W szczególności można przechodzić od Future[Option[A]] do OptionT[Future, A]. Podstawową ideą jest to, że można na tej płaskiej mapie narysować wartość i wyodrębnić wartość typu A.

Ale przed tym, trzeba zrobić swoje typy „prawo kształtu”, tak że obie są Future[Option[Something]]

Oto przykład przy użyciu 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 buduje OptionT[Future, A] dali Future[Option[A]] , podczas gdy liftM[OptionT] uzyskuje to samo, gdy masz Future[A] Typ res jest OptionT[Future, Unit] w tym przypadku, a można uzyskać Future[Option[Unit]] dzwoniąc pod numer run.

2

W celu zrozumienia, wyrażenia po prawej stronie <- muszą mieć zgodne typy. Dzieje się tak, ponieważ dla zrozumienia są to w zasadzie cukier syntaktyczny dla połączeń zagnieżdżonych flatMap. Aby rozwiązać ten problem, możesz mieć zrozumienie zagnieżdżone w innym dla zrozumienia lub możesz użyć metod takich jak get lubw s. Option.

Inną opcją jest użycie monad transformers, ale to wykracza poza zakres tej odpowiedzi.

4

Patrząc na "How does yield work", twój do zrozumienia jest równoznaczne z

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

zasadzie to, co się dzieje, jest to: flatMap na Future definiuje podjąć funkcję zwracającą innej Future. To daje podobny błąd:

<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 => ^

Jeśli chcesz wykonać operację asynchronicznie, czyli naprawdę mapowania Future instancji, trzeba będzie wrócić do Future[Option[X]]. Jak sugeruje Kim, można zagnieździć zrozumieniem:

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" 
    } 
} 

Można również dokonać otrzymany Future zawieść, gdy opcja nie jest zdefiniowana (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" 
    } 
} 
Powiązane problemy