Flatten future inside yield - scala

In the code below x is type Future[Future[Int]] and I need to flatten it to become Future[Int]. How is this possible?
def times(a:Int, b:Int) = Future { a * b }
val x = for {
x1 <- Future { 1 }
x2 <- Future { 2 }
} yield {
val future = times(x1,x2)
future.map { result => result * 2 }
}

Use the same for-comprehension. As times returns Future taking input x1 and x2. You can use same for-comprehension to extract the value out of the future.
def times(a:Int, b:Int) = Future { a * b }
val x = for {
x1 <- Future { 1 }
x2 <- Future { 2 }
result <- times(x1,x2)
} yield (result * 2)
Remember for-comprehension is a syntactic sugar for flatMaps and one final map
The whole thing can be written as
Future { 1 }.flatMap { x1 => Future { 2 }.flatMap { x2 => times(x1, x2).map { result => result * 2 }}}
Warning: As you are creating data independent futures inline (inside the for-comprehension or inside the flatMap). These futures will execute sequentially.
As first and second futures are data independent. They can be executed parallelly if you have created them outside the for-comprehension or flatMap.
Flatten futures
Use flatMap to convert Future[Future[Int]] into Future[Int]
def times(a:Int, b:Int) = Future { a * b }
val x = (for {
x1 <- Future { 1 }
x2 <- Future { 2 }
} yield {
val future = times(x1,x2)
future.map { result => result * 2 }
}).flatMap(identity) // <--- notice flatMap here around the for-comprehension
For clarity above code can be re-written as
def times(a:Int, b:Int) = Future { a * b }
val x = for {
x1 <- Future { 1 }
x2 <- Future { 2 }
} yield times(x1,x2).map(_ * 2)
val finalResult = x.flatMap(identity)
Scala REPL
scala> :paste
// Entering paste mode (ctrl-D to finish)
def times(a:Int, b:Int) = Future { a * b }
val x = for {
x1 <- Future { 1 }
x2 <- Future { 2 }
} yield times(x1,x2).map(_ * 2)
val finalResult = x.flatMap(x => x)
// Exiting paste mode, now interpreting.
scala> finalResult
res0: scala.concurrent.Future[Int] = Future(Success(4))

Related

Scala map is not gettin updated

I've written the following solution in scala for the three sum problem.
However the value in the map sums never gets updated.
Can someone explain why doesn't it get updated?
import collection.mutable.ListBuffer
object Solution {
def threeSum(nums: Array[Int]): List[List[Int]] = {
val sums = Map[Int, ListBuffer[(Int, Int)]]().withDefaultValue(ListBuffer())
for((a, aIndex) <- nums.view.zipWithIndex) {
for((b, bIndex) <- nums.view.zipWithIndex) {
if(aIndex != bIndex){
val sum = a + b
List(a, b).sorted match {
case List(a, b) => {
sums(sum) += ((a, b))
println(sums)
}
}
}
}
}
val result = ListBuffer[List[Int]]()
for(c <- nums){
val neg = c * -1
if(sums.contains(neg)){
val sumSet = sums(neg).toSet
for((a,b) <- sumSet) {
val triplet = List(a, b, c).sorted
result += triplet
}
}
}
result.toList
}
}

Getting compilation error when using for and yield

What is wrong with the following code snippet?
val loginInfoFuture: Future[LoginInfo] = credentialsProvider.authenticate(credentials)
for{loginInfo <- loginInfoFuture}{
println("in loginInfo future")
} yield Future{Ok(Json.toJson(JsonResultError("Invalid Body Type. Need Json")))}
I am seeing error in IDE - Error:(239, 17) ';' expected but 'yield' found.
} yield Ok(Json.toJson(JsonResultError("Invalid Body Type. Need Json")))
I tried a similar piece of code on REPL and that seem to work fine.
scala> import scala.concurrent.Future
import scala.concurrent.Future
scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global
scala> val f:Future[Int] = Future{1}
f: scala.concurrent.Future[Int] = Future(Success(1))
scala> for(f1 <- f) yield f1
res0: scala.concurrent.Future[Int] = Future(<not completed>)
scala>
For reference, below is the full function
def signInUser = silhouette.UserAwareAction.async { implicit request => {
val body: AnyContent = request.body
val jsonBody: Option[JsValue] = body.asJson
jsonBody match {
case Some(json) => {
val userSignin: Option[UserSignin] = json.asOpt[UserSignin] //check if json conforms with UserProfile structure
userSignin match {
case Some(signinInfo) => { //format of JSON is correct
//Get signin info from JSON (email and password)
val credentials: Credentials = Credentials(signinInfo.signinInfo.email, signinInfo.signinInfo.password)
val authInfoRepository = new DelegableAuthInfoRepository(userRepo.passwordRepo)
val passwordHasherRegistory = new PasswordHasherRegistry(userRepo.passwordHasher)
val credentialsProvider = new CredentialsProvider(authInfoRepository, passwordHasherRegistory)
for{loginInfo <- loginInfoFuture}{ //for returns unit. Should use yield
println("in loginInfo future")
} yield Future{Ok(Json.toJson(JsonResultError("Invalid Body Type. Need Json")))}
}
case None => { //No signin info found
Future {
Ok(Json.toJson(JsonResultError("Invalid user. No Login info found")))
}
}
}
}
case None => {//NO Body
Future {
Ok(Json.toJson(JsonResultError("Invalid Body Type. Need Json")))
}
}
} //jsonBody match
}//async
}//def signin
for{loginInfo <- loginInfoFuture}{ //for returns unit. Should use yield
println("in loginInfo future")
} yield Future{Ok(Json.toJson(JsonResultError("Invalid Body Type. Need Json")))}
This is invalid. Your for/yield needs to be in the format:
for {
y <- z
x <- y
//etc
} yield {
//whatever
}
The println after the for but before the yield is throwing you. To get the result of the println inside the for/yield, you could to assign it to a value:
for {
y <- z
a = println(y) // will print out every y
x <- y
//etc
} yield {
//whatever
}
for/yield blocks are stupid like that. At least there are work-arounds though!
The following section is from scala's Future documentation:
def foo(): Unit = {
val f = Future { 5 }
val g = Future { 3 }
val h = for {
x: Int <- f // returns Future(5)
y: Int <- g // returns Future(3)
} yield x + y
}
You on the other hand try to do this:
def foo(): Unit = {
val f = Future { 5 }
val g = Future { 3 }
val h = for {
x: Int <- f // returns Future(5)
y: Int <- g // returns Future(3)
} {
println("whatever") // <<<<<<<<<
} yield x + y
}
The extra block of code that I point is what causing the compilation error which you did not add in your scala repl example.
This is how you can print within a Future:
def foo(): Unit = {
val f = Future {
println("5")
5
}
val g = Future {
println("3")
3
}
val h = for {
x: Int <- f // returns Future(5)
y: Int <- g // returns Future(3)
} yield x + y
}
Error:(239, 17) ';' expected but 'yield' found.
simply means that the for loop definition is wrong
So either with yield
for{loginInfo <- loginInfoFuture
//other conditions and statements
} yield //value to be returned
or without yield
for(loginInfo <- loginInfoFuture){
//value updated
}
are correct for loop definitions

How to resolve a Future inside another Future

I want to create a function similar to the following. Basically the function, say F will create a Future say fut1. When fut1 resolves, then another Future say fut2 should get created inside fut1. The fut2 should return the final value of the function F. The code has to be non-blocking all the way. I have written something like this but the return type is not Future[Int] but Future[Future[Int]]. I understand why this is the case (because map creates a Future) but I am unable to figure out how to return Future[Int] from this code.
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
def fut:Future[Int] = {
val f1 = Future{ 1 } //create a Future
f1.map (x => { //when f1 finishes, create another future
println(x)
val f2 = Future{ 2 }
f2.map(x=> x) //this creates another Future and thus the return is Future[Future[Int]]
})
}
You can achieve this using flat map or for comprehension.
FlatMap-
def futureFunctionWithFlatMap: Future[Int] = {
val f1 = Future {
1
}
f1.flatMap(x => {
println(x)
val f2 = Future {
2
}
f2.map(x => x)
})
}
For Comprehension
def futureFunctionWithForComprehension: Future[Int] = {
for {
f1 <- Future { 1 }
f2 <- {
println(f1)
Future { 2 }
}
} yield f2
}
Use flatMap
val f1 = Future{ 1 } //create a Future
val f2: Future[Int] = f1.flatMap(x => {
//will be triggered only after x is ready
Future{2}
})

Returning response from future inside a yield in Play for Scala

In the following code I have a Play for Scala function with a future inside a yield. I get a compilation error type mismatch; found : scala.concurrent.Future[Nothing] required: play.api.mvc.Result. Shouldn't I flatMap the future inside the yield to return the Ok response?
def f1 = Future { 1 }
def f2 = Future { 2 }
def index = Action.async (parse.json) { request =>
for { x1 <- f1 }
yield {
val f = f2
f.flatMap { result =>
Ok("X")
}
}
}
No, when you say flatMap you are saying that the result returned in the function is a Future[T], which in your example it returns a Result, so just using a map would work, but is not super idiomatic as you have to flatten as you wind up with a Future[Future[Result]]:
(for { x1 <- f1 } yield {
val f = f2
f.map( result =>
Ok("X")
)
}).flatten
What is more idiomatic is to use a for comprehension for the whole thing:
for {
x1 <- f1
x2 <- f2
} yield {
Ok("X")
}

Returning two Scala futures when both end

The method1 below is an attempt to return the results of two Future methods someFutureMethod1 and someFutureMethod2 combined. Any ideas how to make this work? Ideally the method that ends first should wait until the second ends, and then return the future values combined.
def method1 ( s: Seq[Int]): Future [(Int,Int)] = {
s.map { sx =>
val future = someFutureMethod1 (sx)
future.map {
result => result
}
val future2 = someFutureMethod2 (sx)
future2.map {
result2 => result2
}
(result,result2) // <-- the method should return the two results in the future
}
}
def someFutureMethod1 (i: Int) = Future {
i + 1
}
def someFutureMethod2 (i: Int) = Future {
i + 2
}
You combine Future's in a map, so it looks like you have to return a Seq of such Future's:
def method1 ( s: Seq[Int]): Seq[Future [(Int,Int)]] =
s.map { sx =>
val future = someFutureMethod1 (sx)
val future2 = someFutureMethod2 (sx)
future.zip(future2)
}