2014-09-04 10 views
7

Ich versuche, Futures in Scala zu ketten, aber es gibt mir den falschen Rückgabetyp.Chain Scala Futures Rückgabetyp

Ich habe die folgenden Methoden:

def getOneRecordByModel(x:DirectFlight): Future[Option[FlightByDetailModel]] = { 
    select.allowFiltering().where(_.from eqs x.from).and(_.to eqs x.to).and(_.departure eqs x.departure).and(_.arrival eqs x.arrival).and(_.carrier eqs x.airline).and(_.code eqs x.flightCode).one() 
    } 
    def getRecordByUUID(x:FlightByDetailModel): Future[Option[FlightByUUIDModel]] = { 
    select.allowFiltering().where(_.uuid eqs x.uuid).one() 
    } 

    def getUUIDRecordByModel(x:DirectFlight): Future[Option[FlightByUUIDModel]] = { 
     getOneRecordByModel(x) andThen { 
     case Success(Some(flight)) => getRecordByUUID(flight) 
     case Success(x) => Success(x) 
     case Failure(x) => Failure(x) 
     } 
    } 

Aber jetzt bekomme ich die Fehlermeldung, dass die getUUIDRecordByModel Rückgabetyp ist Future[Option[FlightByDetailModel]]

Wie verketten ich sie richtig?

+0

Wenn Sie And verwenden, dann ändern Sie den Rückgabetyp nicht. Sie möchten FlatMap oder Map abhängig vom Rückgabetyp der anderen Methode. – monkjack

+2

Der 'andThen'-Kombinator ist ausschließlich zum Nebeneffekt gedacht. Es gibt immer die "Zukunft", an der es aufgerufen wird, unverändert zurück. Wie von den anderen erwähnt, sollten 'map' und/oder' flatMap' genau das sein, wonach Sie suchen. – cmbaxter

Antwort

8

Ich würde stattdessen flatMap verwenden.

def getUUIDRecordByModel(x:DirectFlight): Future[Option[FlightByUUIDModel]] = { 
    getOneRecordByModel(x) flatMap { 
     case Some(flight) => getRecordByUUID(flight) 
     case None => Future.successful(None) 
    } 
} 

andThen wendet eine Nebenfunktion zu bewirken und gibt den ursprünglichen Future, nicht auf die innere.

+0

Das ist es. Vielen Dank! – elmalto

3

Sie können es schön mit Scalaz Monade Transformatoren tun, OptionT genauer. Sie können Reed schöne Reihe von Artikeln und präziser Sie benötigen diese: http://eed3si9n.com/learning-scalaz/Monad+transformers.html#Monad+transformers

Dieses zu gut ist: http://noelwelsh.com/programming/2013/12/20/scalaz-monad-transformers/

def getOneRecordByModel(x:DirectFlight): Future[Option[FlightByDetailModel]] = ??? 
    def getRecordByUUID(x:FlightByDetailModel): Future[Option[FlightByUUIDModel]] = ??? 

    def getUUIDRecordByModel(x:DirectFlight): Future[Option[FlightByUUIDModel]] = { 
    import scalaz.OptionT._ 

    val flightByUUID = for { 
     flightByDetailModel <- optionT(getOneRecordByModel(x)) 
     flightByUUIDModel <- optionT(getRecordByUUID(flightByDetailModel)) 
    } yield flightByUUIDModel 

    flightByUUID.run 
    } 

zu können optionT verwenden, mit scala.concurrent.Future Sie benötigen Functor und Monad Instanzen in Umfang

import scala.concurrent.Future 

    object FutureMonadAndFunctor { 

    import scalaz.Monad 

    implicit def FutureM(implicit ec: ExecutionContext): Monad[Future] = new Monad[Future] { 
     def point[A](a: => A): Future[A] = Future(a) 
     def bind[A, B](fa: Future[A])(f: (A) => Future[B]): Future[B] = fa flatMap f 
    } 

    implicit def FutureF(implicit ec: ExecutionContext): Functor[Future] = new Functor[Future]{ 
     def map[A, B](fa: Future[A])(f: (A) => B): Future[B] = fa map f 
    } 
    } 

    import scala.concurrent.ExecutionContext.Implicits.global 

    implicit val F = FutureMonadAndFunctor.FutureF 
    implicit val M = FutureMonadAndFunctor.FutureM 
+0

Vielen Dank für die Einsicht. Ich bin jetzt schon mehrmals auf Scalaz gestoßen. Ich denke, es ist Zeit für mich, etwas zu lernen. – elmalto

2

Eine einfache Lösung zu sein, ist flatMap für Zusammensetzung zu verwenden, statt andthen, die für den Umgang mit Nebenwirkungen eher spezialisiert ist:

getOneRecordByModel(x) flatMap { 
    ... 
} 

Für die Arbeit mit Futures fand ich es hilfreich, this page mehrmals zu lesen.

5

Diese Lösung und die 2 darüber sind im Grunde gleich. Sie schlagen die einfache Antwort der Zusammensetzung von flatMaps vor. Dies ist gut für einmalige Lösungen.

for { 
    oUuid <- getOneRecordByModel(x) 
    oFlight <- oUuid.map(getRecordByUUID).getOrElse(Future.successful(None)) 
} yield oFlight 

I gegeben vermuten die Methodensignaturen, Sie gehen diese Strategie viel zu verwenden. Wenn dies der Fall ist, wird die obige Antwort von Eugene Zhulenev empfohlen (die eine funktionellere Lösung darstellt). Dachte Monad Transformers ein bisschen einschüchternd auf den ersten Blick aussehen kann, hier das Stück Code:

val flightByUUID = for { 
    flightByDetailModel <- optionT(getOneRecordByModel(x)) 
    flightByUUIDModel <- optionT(getRecordByUUID(flightByDetailModel)) 
} yield flightByUUIDModel 

flightByUUID.run // this line grabs you a Future[Option[T]] 

ist sehr einfach und skalierbar wie Sie zusätzliche Komplexität starten. Hoffentlich hilft dir das.

+0

funktioniert auch sehr vielen Dank. Leider kann ich nur eine Antwort als richtig auswählen. – elmalto

Verwandte Themen