Scala : Better way to handle returning Future from Yeild to avoid future of future - scala

Consider the below code snippet:
In this, I am trying to get values from future using 'For - yield' comprehension. Now in yield method, I need to do a check which makes the call to a function fallbackResult which returns a future and hence return type of getData becomes 'Future[Future[Option[Int]]]' rather than 'Future[Option[Int]]'. How could I make this with a better way? (I did use Map & FlatMap's, but the code little ugly due to nesting Maps and FlatMaps)
def getData(): Future[Future[Option[Int]]] = {
/* These are two future vectors. Ignore the Objects */
val substanceTableF: Future[Vector[OverviewPageTableRowModel]] = getSubstanceTable(substanceIds, propertyId, dataRange)
val mixtureTableF: Future[Vector[OverviewPageTableRowModel]] = getMixtureTableForSubstanceCombination(substanceIds, propertyId, dataRange)
/* I have put for yeild to get values from futures.*/
for {
substanceTable <- substanceTableF
mixtureTable <- mixtureTableF
} yield {
if(substanceTable.isEmpty && mixtureTable.isEmpty) {
val resultF = fallbackResult()
resultF.map(result => {Some(result)})
} else {
Future.successful(Some(100))
}
}
}
private def fallbackResult(): Future[Int] = {
// This method returns future of int
}

There are a lot of ways to handle this, but the key thing is to move your yield logic into your for comprehension. One way to do this is as follows:
for {
substanceTable <- substanceTableF
mixtureTable <- mixtureTableF
result <- (substanceTable.headOption orElse mixtureTable.headOption)
.map(_ => Future.successful(Some(100)))
.getOrElse(fallbackResult)
} yield result

Id put the code inside the for-coprehension:
for {
substanceTable <- substanceTableF
mixtureTable <- mixtureTableF
result <- {
if (substanceTable.isEmpty && mixtureTable.isEmpty)
fallbackResult()
else
Future.successful(Some(100))
}
} yield result

Related

Execute operation on Future's value for its side effects, discarding result and retaining original value, but retaining order of operation

Say I have the following operations that must proceed in order:
Get blog post
Post analytics
Forward blog post
In code it may look like this:
val blogPostFut: Future[BlogPost] = blogService.getPost(postId)
val afterAnalytics: Future[BlogPost] = blogPostFut.flatMap(blogPost =>
val ignoredResponse: Future[Analytics] = analyticsService.sendAnalytics(blogPost)
ignoredResponse.map(_ => blogPost) // <-- THIS BOTHERS ME
)
val finalValue: Future[ForwardResult] = afterAnalytics.flatMap(blogPost =>
forwardService.forward(blogPost)
)
I am bothered that, in order to ensure proper ordering of execution, I have to pass forward blogPost within ignoredResponse in order to ensure it is available for step 3.
I'd love if I could do something like this:
blogPostFut.magicalFlatMap(analyticsService.sendAnalytics)
Where magicalFlatMap might be implemented like so:
// pseudocode
def magicalFlatMap[A,B](f: A => Future[B]): Future[A] = f().map(_ => this.value)
Does magicalFlatMap exist in either the Scala stdlib or in Cats? Is it possible to map a Future for side effects while automatically retaining the value of the original Future and strict ordering of operations?
magicalFlatMap seems to be cats.FlatMap#flatTap
https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/FlatMap.scala#L150
Try Future.andThen for side-effects
for {
blogPost <- blogService.getPost(postId).andThen { case Success(post) => analyticsService.sendAnalytics(post) }
finalValue <- forwardService.forward(blogPost)
} yield {
finalValue
}
Here is a dummy example
val result = for {
v1 <- Future(1)
v2 <- Future(v1 + 2).andThen { case Success(v) => println(v) }
v3 <- Future(v1 + v2)
} yield {
v3
}
result.foreach(println)
which should output
3
4
We could also do
for {
blogPost <- blogService.getPost(postId)
_ <- analyticsService.sendAnalytics(blogPost)
finalValue <- forwardService.forward(blogPost)
} yield {
finalValue
}
however in this case failure in analyticsService.sendAnalytics(blogPost) would short-circuit the whole for-comprehension which might not be desirable.

How to convert a class T object into a Future[T] object in akka

I am trying to do something with akka and scala and i am new to it. I want to append the result of an 'Any' Future into one of the fields in the class
So i have class called T defined as
class T {
var a: String =_
var result = List[Any]= List()
}
Now i have a variable which receives a future value
var futureResult:Future[Any] = //receives a future value
in a function called addResult which takes an T object and returns a future T object.
def addResult(obj1:T):Future[T] ={
var obj2:T = new T()
obj2.a = obj1.a
obj2.result = obj1.result
//I want to append results of futureResult into obj2.result when it is completed
// obj2.result = futureResult :: obj2.result
return Future(obj2)
}
I have to finally call this function from a flow.
val b = Flow[T].mapAsync[T](1)(addResult(_))
First, as #riccardo.cardin noted using T as a name of a class is very bad idea, because of T usual points to generic.
However, you can put any logic in Future with a simple closure, in your case, it looks like this:
Future {
new T("some string", 1 :: 2 :: 3 :: Nil)
}
After that, you can combine asynchronous computation via flatMap, map or with for comprehensive or even use cats black magic with |#|:
for {
f1 <- future1()
f2 <- future2(f1.id)
} yield f2.name
(future1 |#| future2) map { _ - _ }
In your case this transformation depends from logic of your code.
You can change the commented line like this:
futureResult.map { res =>
obj2.result = res :: obj2.result
obj2
}
And then you won't need the last line.

Scala for comprehension how to avoid creating of Future when passing results

I'm using Playframework and Slick async features, but not sure how to work inline with results from Future returning method in one for comprehension. Right now I'm doing it such way:
def getWordDefinitions(checkedWordsIds: List[CheckedWord]) : Future[List[WordDefinition]] = {
val ids = checkedWordsIds.map(_.wordId)
for {
translations <- translationRepo.findByIds(ids)
translations2 <- Future(sortByHowManyChecks(checkedWordsIds, translations))
wordDefinitionsList <- Future(translations2.map(translation => WordDefinition(translation._2.english, translation._2.translation)))
} yield {
wordDefinitionsList
}
}
I want to know how to get rid off translations2 <- Future(), besides move it to the function (or wrap function into another which return Future).
sortByHowManyChecks function returns Map[Long, TranslationObject] which is in 3rd party library.
In your case you can simply write it this way:
def getWordDefinitions(checkedWordsIds: List[CheckedWord]) : Future[List[WordDefinition]] = {
val ids = checkedWordsIds.map(_.wordId)
for {
translations <- translationRepo.findByIds(ids)
translations2 = sortByHowManyChecks(checkedWordsIds, translations)
} yield translations2.map(translation => WordDefinition(translation._2.english, translation._2.translation))
}
What do you think about not using yield at all? Not sure if I got every return statement right.
def getWordDefinitions(checkedWordsIds: List[CheckedWord]) : Future[List[WordDefinition]] = {
val ids = checkedWordsIds.map(_.wordId)
translationRepo.findByIds(ids)
.map(translations => sortByHowManyChecks(checkedWordsIds, translations))
.map(translation => WordDefinition(translation._2.english, translation._2.translation))
}

Another scala Futures composition puzzler

I am migrating code from a synchronous to an async style. The problem is fairly simple: call a series of functions, and stop with the first one to return a non-error result (returning that value, otherwise the last computed value). I started with:
def find(fs: Seq[Function0[Int]], result: Int = -1): Int = {
if (fs.isEmpty) result
else {
val res = fs.head()
if (res != 0) res
else find(fs.tail, res)
}
}
However, if the functions become async, (i.e. return Future[Int]) I can't get the right invocation. For example,
def ffind(ffs: Seq[Function0[Future[Int]]], result: Future[Int] = Future { -1 }): Future[Int] = {
if (ffs.isEmpty) result
else ffind(ffs.tail, ffs.head())
}
works great, but it evaluates all the functions, regardless of return value. But something like:
def findBad(ffs: Seq[Function0[Future[Int]]], result: Future[Int] = Future { -1 }): Future[Int] = {
if (ffs.isEmpty) result
else {
ffs.head() map { res =>
if (res != 0) res
else findBad(ffs.tail, Future(res))
}
}
}
doesn't type-check. Any suggestions? We can suppose that each call of a function is expensive, so none should be called twice, nor any after the first 'successful' call in the sequence. TIA
Here's why it doesn't type-check: findBad returns a Future[Int], but mapping res into an invocation of findBad would result in a Future[Future[Int]]. You need to change map into flatMap. Note that now you also need to wrap res from the first condition (if res != 0) into a Future too, so that both branches return a Future. Here's the code:
ffs.head() flatMap { res =>
if (res != 0) Future.succesful(res)
else findBad(ffs.tail, Future(res))
}
BTW if you want to run them all and return whichever completes first, disregarding all the rest, then it's a bit different problem (see here), but you say that each function call is expensive so I doubt that's what you are trying to do.

How to convert this to using Futures, not sure how to use Future.Traverse

I have the following old code that was previously NOT returning futures, but it is now:
for (rg <- effectedRoleGroups) {
val pm = securityService.generateABC(rg.id)
redisService.setABC(rg.id, pm)
}
Now both securityService and redisService are returning Futures.
I know I can do this:
for {
result <- Future.traverse(effectedRoleGroups)(rg -> securityService.generateABC(rg.id)
}
But I am not sure how I would use the result in this case to pass to my other service.
Asuming you want to ignore result of Futures as you use foreach loop you can do this like this:
for (rg <- effectedRoleGroups) {
for {
pm <- securityService.generateABC(rg.id)
_ <- redisService.setABC(rg.id, pm)
} yield ()
}
Which is equivalent to:
for (rg <- effectedRoleGroups) {
securityService.generateABC(rg.id).flatMap(pm => redisService.setABC(rg.id, pm))
}
Using Future.traverse:
val results: Future[List[ResultFromRedis]]] = Future.traverse(effectedRoleGroups)(rg =>
securityService.generateABC(rg.id).flatMap(pm => redisService.setABC(rg.id, pm)))
This will give you Future of List of results that redisService.setABC will yield.
Future.traverse(..some traversable..)(...)
returns Future of transformed traversable.
So you actually don't need to use for here. Instead you can do:
val resultFuture = Future.traverse(effectedRoleGroups)(rg -> securityService.generateABC(rg.id))
If you still want to use for you need yield operator:
val resultFuture = for {
result <- Future.traverse(effectedRoleGroups)(rg -> securityService.generateABC(rg.id))
} yield result