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?
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.
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.
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.
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.
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.
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With