2014-09-04 5 views
7

Я пытаюсь связать Futures в Scala, но это дает мне неправильный тип возврата.Chain Scala Futures return type

У меня есть следующие методы:

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

Но теперь я получаю ошибку, что тип getUUIDRecordByModel возвращения Future[Option[FlightByDetailModel]]

Как приковать их правильно?

+0

Когда вы используете, и затем вы не меняете тип возврата. Вы хотите flatMap или карту в зависимости от типа возврата другого метода. – monkjack

+2

Комбинатор 'andThen' предназначен исключительно для побочного эффекта. Он всегда возвращает «Будущее», к которому он обращается, без изменений. Как упоминалось другими, 'map' и/или' flatMap' должны быть тем, что вы ищете. – cmbaxter

ответ

8

Вместо этого я бы использовал flatMap.

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

andThen применяет функцию бокового осуществления и возвращает оригинальныеFuture, а не внутренней.

+0

Вот оно. Большое спасибо! – elmalto

3

Вы можете сделать это красиво с помощью трансформаторов monad с плазазом, опционально. Вы можете камыш хороший набор статей, и более конкретно вам нужно это один: http://eed3si9n.com/learning-scalaz/Monad+transformers.html#Monad+transformers

Это один хорошо: 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 
    } 

Чтобы иметь возможность использовать optionT с scala.concurrent.Future вам нужно Functor и примеры Монады, чтобы находиться в области видимости

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

Благодарим вас за понимание. Я уже несколько раз встречался с Скалазом. Думаю, мне пора учиться. – elmalto

2

простое решение заключается в использовании flatMap для композиции вместо andThen, что является довольно специализированным для борьбы с побочными эффектами:

getOneRecordByModel(x) flatMap { 
    ... 
} 

Для работы с фьючерсами я счел полезным прочитать this page несколько раз.

5

Это решение и 2 над ним, фактически те же самые. Они предлагают простой ответ композиции flatMaps. Это хорошо для одноразовых решений.

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

Я подозреваю, что данный метод подписи, вы собираетесь использовать эту стратегию много. Если это так, рекомендуется ответить @Eugene Zhulenev выше (который является более функциональным решением). Мысли Монада Трансформаторы могут выглядеть немного пугающим на первый взгляд, фрагмент кода здесь:

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

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

Это очень простой и масштабируемой, как вы начинаете добавлять сложность. Надеюсь, это поможет вам.

+0

работает, спасибо вам большое. К сожалению, я могу выбрать только один ответ как правильный. – elmalto