2016-05-26 16 views
5
object Main extends App { 
    val p1 = Promise[Option[String]]() 
    val p2 = Promise[Option[String]]() 
    val f1 = p1.future 
    val f2 = p2.future 

    val res = (for{ 
    file1Opt <- f1 
    file2Opt <- f2 
    file1 <- file1Opt 
    file2 <- file2Opt 
    } yield { 
    combineFiles(file1, file2) 
    }).fallbackTo(Future.successful("Files not found")) 

    Thread.sleep(2000) 
    println("XXXXXXXXXXXXXXXXXXX") 

    p1.success(Some("file one")) 
    p2.success(Some("file two")) 

    val finalData = res.map(s => 
    s + " " + "add more data to the file" 
) 

    finalData.map(println(_)) 

    def combineFiles(f1: String, f2: String): String = { 
    f1 + " " + f2 
    } 
} 

Mam dwie funkcje, które zwracają Future[Option[String]] i potrzebuję połączyć dwa ciągi w jeden ciąg.Scala dla zrozumienia z przyszłością i opcjami

Chcę, aby dane wyjściowe były kombinacją dwóch łańcuchów i stopek: "plik jeden plik dwa dodają więcej danych do pliku" lub domyślnie, gdy jeden lub oba z s zwrócą None: "Pliki nie znaleziono dodaj więcej danych do pliku".

Jak można to osiągnąć?

błąd kompilatora:

Error:(16, 11) type mismatch; 
found : Option[String] 
required: scala.concurrent.Future[?] 
file1 <- file1Opt 
    ^

Odpowiedz

6

Cóż, nie robiąc nic nadzwyczajnego, jak monadowe transformatory czy inne rzeczy, można po prostu zagnieździć się w zrozumieniu for. Będzie bardziej rozwlekły, ale bez dodatkowych zależności.

val res = (for{ 
    file1Opt <- f1 
    file2Opt <- f2 
} yield for { 
    file1 <- file1Opt 
    file2 <- file2Opt 
} yield combineFiles(file1, file2)) 
.fallbackTo(Future.successful(Some("Files not found"))) 
//or, alternatively, .fallbackTo(Future.successful(None)) 

Ostatecznie tu problemem jest to, że starają się połączyć Future i Option w jednym for zrozumieniem. To po prostu nie działa z powodów wymienionych przez innych respondentów. Zagnieżdżanie działa jednak dobrze.

Wadą zagnieżdżania jest to, że kończy się to bardzo złożonymi strukturami danych, które mogą nie być łatwe do użycia w innym miejscu w programie. Powinieneś pomyśleć o tym, jak je spłaszczyć, tj. Przechodząc od Future[Option[String]] do zaledwie Future[String]. Czy w twoim przypadku możesz zrobić coś takiego: res.map(_.getOrElse("")).

Dobra, może 2 poziomy zagnieżdżania są w porządku, ale zagnieżdżasz się więcej, rozważ spłaszczenie tej hierarchii, zanim pozwolisz swoim współpracownikom sobie z nią poradzić. :)

1

myślę dokładnie ten problem jest pokryta this 47deg blog post, jak również w this one: monady nie komponować, więc trzeba transformator z jednej monady do drugiego, ponieważ nie ma operacji flatMap, która byłaby (płasko) mapowana na Future na Option.

4

Podobnie jak alf wymieniony w odpowiedzi, można użyć do tego celu transformatorów monad, w tym przypadku OptionT.

Przykładem użyciu cats:

import scala.concurrent.Future 
import scala.concurrent.ExecutionContext.Implicits.global 
import cats.data.OptionT 
import cats.implicits._ 

val file1: Future[Option[String]] = Future.successful(Some("file1")) 
val file2: Future[Option[String]] = Future.successful(Some("file2")) 

val combinedOT: OptionT[Future, String] = 
    for { 
    f1 <- OptionT(file1) 
    f2 <- OptionT(file2) 
    } yield s"$f1 $f2" 

val combinedFO: Future[Option[String]] = combinedOT.value 
val combinedF: Future[String] = combinedOT.getOrElse("Files not found") 

Zauważ, że jeśli używasz koty, można wymienić na zrozumienie w combinedOT2 za pomocą konstruktora kartezjańskich (The |@|), ponieważ file2 nie zależy file1:

val combinedOT2: Future[Option[String]] = 
    (OptionT(file1) |@| OptionT(file2)).map(_ + " " + _).value 

można nadal używać fallbackTo jeśli „kombinowane” Future zawodzi, mimo iż jest to prawdopodobnie lepiej użyć recover lub recoverWith, aby faktycznie sprawdzić, które Throwable s chcesz odzyskać.

Powiązane problemy