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

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

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.

Adding Futures to an immutable Seq and returning it

I feel like this is not that difficult but I'm struggling with the futures and adding objects or Ints to an immutable Seq.
def createCopyOfProcessTemplate(processTemplateId: Int): Action[AnyContent] = Action.async {
//val copies = Seq()
processTemplateDTO.createCopyOfProcessTemplate(processTemplateId).flatMap { process =>
processTemplateDTO.getProcessStepTemplates(processTemplateId).map { steps =>
steps.foreach(processStep =>
copy: Future[Option[ProcessTemplateModel] = processTemplateDTO.createCopyOfStepTemplates(processTemplateId, process.get.id.get, processStep))
//Seq should look something like this: [{processStep.id, copy.id},{processStep.id, copy.id},...] or [[processStep.id, copy.id],[processStep.id, copy.id],...]
}
Ok(Json.obj("copies" -> copies))
}
Where do I have to define the seq and how should I return it since it's handling Futures ?
Any ideas? Thanks in advance!
You can use Future.sequence to convert List[Future[A]] into Future[List[A]] and return as result. First, do not use steps.foreach with copy variable defined, instead use steps.map to get ProcessTemplateModel as result from processTemplateDTO.createCopyOfStepTemplates, and map will return List of future result - List[Future[Option[ProcessTemplateModel]]. Then you can convert the result with Future.sequence and finally return as Json object.
val copies:List[Future[Option[ProcessTemplateModel]] = processTemplateDTO.createCopyOfProcessTemplate(processTemplateId).flatMap {
process =>processTemplateDTO.getProcessStepTemplates(processTemplateId).map { steps =>
steps.map(processStep =>
processTemplateDTO.createCopyOfStepTemplates(processTemplateId, process.get.id.get, processStep))
}
Future.sequence(copies).map{ result =>
Ok(Json.obj("copies" -> result))
}

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

how to use inner future value in outer future in scala

Hi I have two Futures FutureA and FutureB i want to take a value from FutureB and use it in FutureA but all i am getting is null
here is code
futureA = ask(ActorA, MessageA).mapTo[Int]
FutureB = ask(ActorB, MessageB).mapTo[Int]
var someResult=0
futureA.map {
result =>
FutureB.map { x =>
someResult=x//suppose it will retun 5
}
someResult=someResult+1//it should give 6
println(someResult)//here i am getting 1
}
I want someResult to return 6 but it is returning 1
how can I achieve that
I do not want to use blocking call await.result
Don't use var, especially with futures. That's code smell and asking for trouble.
futureA
.flatMap { _ => FutureB }
.map { _ + 1 }
.onSuccess(println)
Dima's answer is absolutely correct. You're not assigning the result of FutureB.map so it's basically a side effect potentially executed in a background thread without your code knowing. You're likely to reach the print statement at runtime before the variable gets incremented, but maybe not, you can't know.
The right way of doing it is to use immutable variables and to chain your futures using flatMap/map.
Say you have a function called someFunction that takes two Ints and return a Result type : def someFunction(a: Int, b: Int): Result. You can then write the following code:
val result: Future[Result] =
futureA.flatMap { a =>
FutureB.map { b =>
someFunction(a, b)
}
}
You can also write it using a for-comprehension which compile to exactly the same thing but is the standard way of doing it:
val result: Future[Result] =
for {
a <- futureA
b <- FutureB
} yield someFunction(a, b)
Hope this answers your question.

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

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