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
Related
Below is a memoized version of Fibonacci function implemented in ZIO.
If the highlighted line _ <- memo.get is commented out the function fib results in a Stackoverflow error. If it is un-commented the code works as expected.
Why does this get call on the memo Ref is needed to make this function work? and why without it we are running into a stackoverflow error?
object Main extends ZIOAppDefault {
private def compute(num: Int, memo: Ref[Map[Int, Int]]): UIO[Int] = for {
_ <- memo.get // commenting this line will result in Stackoverflow Error
a1 <- fib(num - 1, memo)
a2 <- fib(num - 2, memo)
} yield a1 + a2
private def fib(num: Int, memo: Ref[Map[Int, Int]]): UIO[Int] = {
if(num <= 1)
ZIO.succeed(1)
else
for {
map <- memo.get
res <- map.get(num).map(x => ZIO.succeed(x)).getOrElse(compute(num, memo))
} yield res
}
private def calculate(num: Int) = for {
map <- Ref.make[Map[Int, Int]](Map())
res <- fib(num, map)
} yield res
override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = calculate(9).debug
}
I'm having this issue where I use a for-comprehension in Scala to chain some Futures, and then get an instance of a class with some of those values. The problem is that the val I assign the value to, is of type Future[MyClass] instead of MyClass and I can't seem to figure out why.
The code is something like this:
val future = someService.someFutureReturningMethod()
val x = for{
a <- future
b <- someOtherService.someOtherFutureMethod(a)
} yield {
MyClass(b.sth, b.sthElse)
}
The problem here is that x ends up being of type Future[MyClass] and not MyClass and I can't seem to figure out why.
That behavior is correct, you can use for comprehension because Future[T] understands flatMap and map methods.
The following code
val futureA = Future.successful(1)
val futureB = Future.successful(2)
val futureC = Future.successful(3)
val x1 = for {
a <- futureA
b <- futureB
c <- futureC
} yield {
a + b + c
}
It is compiled to
val x2 = futureA.flatMap {
a => futureB.flatMap {
b => futureC.map {
c => a + b + c
}
}
}
A call to Future.flatMap or Future.map is a Future. (it is the same with Option, Try, Either, etc)
If you want the result you need to wait for it.
Await.result(x, Duration(10, TimeUnit.SECONDS))
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}
})
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))
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")
}