2017-12-08 4 views
0

Ich würde gerne über einen Vektor, den ich aus einer Zukunft bekomme. Ich möchte, dass das Folgende funktioniert, aber es gibt einen Typenkonflikt-Compiler-Fehler in der Zeile user <- usersToReview.Mischen von Futures und Vektoren in einem für das Verständnis

import scala.concurrent.{Await, Future} 
import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.Await 
import scala.concurrent.duration._ 

object Main3 { 
    var counter = 0 

    val fUsersToReview: Future[Vector[String]] = Future {Vector("u1", "u2", "u3")} 

    def doIt(user: String): Future[Int] = { 
    counter = counter + 1 
    Future.successful(counter) 
    } 

    def main(args: Array[String]): Unit = { 
    val result:Future[Vector[Int]] = for { 
     usersToReview ← fUsersToReview 
     user ← usersToReview 
     msg_id ← doIt(user) 
    } yield { 
     msg_id 
    } 

    println(Await.result(result, 1.second)) 
    } 
} 

Folgendes funktioniert, wirkt aber sehr klobig. Vorschläge zur Verbesserung? Diese

def main(args: Array[String]): Unit = { 
    val result:Future[Vector[Int]] = (for { 
     usersToReview ← fUsersToReview 
    } yield { 

     Future.sequence(
     for (u ← usersToReview) yield { 
      doIt(u) 
     } 
    ) 
    }).flatMap(identity) 

    println(Await.result(result, 1.second)) 
    } 
} 

Antwort

0

Dank den Erkenntnissen von adice727's Antwort habe ich eine Lösung für das Problem. Es beinhaltet Refactoring doIt, um den Vektor zu nehmen. Durch Verschieben der Handhabung des Vektors auf doIt kommt der Vektor aus dem Verständnis heraus. Sobald es nur Futures handhabt, funktioniert es wie erwartet.

Ich hatte meine ursprüngliche Post ein wenig vereinfacht, so dass das folgende eine weitere Ebene im Verständnis enthält und doIt tatsächlich etwas mit den Daten, die es passiert ist. Der Hintergrund ist, dass fMaintainers, fUsersToReview und doIt alle auf einer Datenbank handeln.

val fMaintainers: Future[Vector[String]] = Future {Vector("m1", "m2", "m3")} 

val fUsersToReview: Future[Vector[String]] = Future {Vector("u1", "u2", "u3")} 

def doIt(users: Vector[String], maintainers: Vector[String]): Future[Vector[(String, String)]] = { 
    Future.sequence(for (u ← users) yield { 
    Future.successful((u, maintainers.head)) 
    }) 
} 

val result: Future[Vector[(String, String)]] = for { 
    maintainers ← fMaintainers 
    usersToReview ← fUsersToReview 
    msg_id ← doIt(usersToReview, maintainers) 
} yield { 
    msg_id 
} 

println(Await.result(result, 1.second)) 

schließlich auf adrice727 Vorschlag Verfolgung, die manchmal für-Comprehensions sind nicht das richtige Werkzeug, hier ist es mit Karten:

val result:Future[Vector[(String, String)]] = 
     fMaintainers.flatMap { m ⇒ fUsersToReview.flatMap { u ⇒ doIt(u, m) } } 
0

ist ein Fall, in dem es wahrscheinlich sauberer kein for-comprehension verwenden und stattdessen nur verwenden map:

val result: Future[Vector[Int]] = fUsersToReview.map(_.map(doIt(_))) 

Für Dinge wie Option und Either, können Sie Monade Transformatoren wie OptionT und EitherT verwenden, aber es gibt nichts für Vector.

+0

Dieses schöne ist, außer dass doit eine zukünftige Entwicklung. Der Rückgabetyp dieser Lösung ist also tatsächlich Future [Vector [Future [Int]]]. Ich denke, was ich gelernt habe ist, dass die Metapher eines For-Verständnisses wie eine verschachtelte Schleife zusammenbricht. Eine verschachtelte Schleife kann über Integer in einer Schleife und Zeichen in einer anderen Schleife iterieren, aber bei Nachvollzählungen müssen Sie den Typ auswählen und konsistent sein. Wenn Sie das nicht können, brechen Sie die Maps und FlatMaps aus (oder suchen Sie nach Möglichkeiten, Ihre Typen zu konvertieren). – bwbecker

Verwandte Themen