Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chain Scala Futures return type

Tags:

scala

future

I am trying to chain Futures in Scala but it is giving me the wrong return type.

I have the following methods:

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

But now I get the error that the getUUIDRecordByModel return type is Future[Option[FlightByDetailModel]]

How do I chain them correctly?

like image 730
elmalto Avatar asked Sep 04 '14 19:09

elmalto


People also ask

How can I get result from Future Scala?

You need to provide a Future[Result] to this function. Now you can just make transformations from your Future[List[Int]] to Future[Result] using Scala's map, flatMap, for-comprehension or async/await. Here an example from Play Framework documentation. Show activity on this post.

How do Futures work in Scala?

Future represents a result of an asynchronous computation that may or may not be available yet. When we create a new Future, Scala spawns a new thread and executes its code. Once the execution is finished, the result of the computation (value or exception) will be assigned to the Future.

Is Scala Future a Monad?

yes, then it's a monad. @ElectricCoffee no. @PabloFernandez Scala's flatMap is Haskell's >>= , and Scala's for-comprehensions are equivalent to Haskell's do notation.

What is ExecutionContext in Scala?

An ExecutionContext can execute program logic asynchronously, typically but not necessarily on a thread pool. A general purpose ExecutionContext must be asynchronous in executing any Runnable that is passed into its execute -method.


4 Answers

I would use flatMap instead.

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

andThen applies a side-effecting function and returns the original Future, not the inner one.

like image 85
Michael Zajac Avatar answered Oct 03 '22 19:10

Michael Zajac


This solution and the 2 above it, are effectively the same. They suggest the simple answer of composition of flatMaps. This is good for one-off solutions.

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

I suspect given the method signatures, you're going to be using this strategy a lot. If that's the case, @Eugene Zhulenev's answer above (which is a more functional solution) is recommended. Thought Monad Transformers can look a bit intimidating at first glance, the chunk of code here:

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

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

Is very simple, and scalable as you start adding complexity. Hopefully this helps you.

like image 36
jordan3 Avatar answered Oct 03 '22 19:10

jordan3


You can do it nicely with scalaz monad transformers, optionT more specifically. You can reed nice set of articles, and more specific you need this one: http://eed3si9n.com/learning-scalaz/Monad+transformers.html#Monad+transformers

This one is good to: 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
  }

To be able to use optionT with scala.concurrent.Future you need Functor and Monad instances to be in scope

  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
like image 43
Eugene Zhulenev Avatar answered Oct 03 '22 20:10

Eugene Zhulenev


A simple solution is to use flatMap for composition instead of andThen, which is rather specialized for dealing with side effects:

getOneRecordByModel(x) flatMap { 
    ...
}

For working with futures, I found it helpful to read this page several times.

like image 40
Spiro Michaylov Avatar answered Oct 03 '22 18:10

Spiro Michaylov