Nested For Comprehensions with Futures - scala

Given:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
def f: Future[Either[String, Int]] = Future { Right(100)}
def plus10(x: Int): Future[Either[String, Int]] =
Future { Right(x + 10) }
I'm trying to chain the Future[...] together as so:
scala> for {
| x <- f
| y <- for { a <- x.right } yield plus10(a)
| } yield y
<console>:17: error: value map is not a member of Product with
Serializable with
scala.util.Either[String,scala.concurrent.Future[Either[String,Int]]]
y <- for { a <- x.right } yield plus10(a)
^
I am expecting to get: Future{Right(100)} as a result, but I get the above compile-time error.
Travis Brown gave an excellent answer on how to use Monad Transformers to fix my code here. However, how can I fix my code without Monad Transformers?

Turns out that I can use Either#fold:
scala> for {
| a <- f
| b <- a.fold(_ => Future { Left("bad") }, xx => plus10(xx) )
| } yield b
res16: scala.concurrent.Future[Either[String,Int]] =
scala.concurrent.impl.Promise$DefaultPromise#67fc2aad
scala> res16.value
res17: Option[scala.util.Try[Either[String,Int]]] =
Some(Success(Right(110)))

I was about to answer when yours appeared, but you might still look at this:
val res = for {
x <- f
y <- x.fold(x => Future{Left(x)}, plus10)
} yield y
It is a little more concise on the right side and keeps the left side.

Related

Scala shortcut first solution in a for-comprehension list

I've got a coding draft which works so far as it delivers the correct answer. But from the esthetics side, it could be improved, my guess!
Aim: Find first solution in a list of many possible solutions. When found first solution, don't calculate further. In real-world application, the calculation of each solution/non-solution might be more complex for sure.
Don't like: The Solution=Left and NoSolution=Right aliasing is contra-intuitive, since Right normally stands for success and here Left and Right are swapped (since technically when using Either only Left shortcuts the for-comprehension list)
Is there a nice way to improve this implementation? or another solution?
package playground
object Test {
def main(args: Array[String]): Unit = {
test
}
val Solution = Left
val NoSolution = Right
def test: Unit = {
{
// Find the first solution in a list of computations and print it out
val result = for {
_ <- if (1 == 2) Solution("impossible") else NoSolution()
_ <- NoSolution()
_ <- NoSolution(3)
_ <- Solution("*** Solution 1 ***")
_ <- NoSolution("oh no")
_ <- Solution("*** Solution 2 ***")
x <- NoSolution("no, no")
} yield x
if (result.isLeft)
println(result.merge) // Prints: *** Solution 1 ***
}
}
}
So you're looking for something that's "monaduck": i.e. has flatMap/map but doesn't necessarily obey any monadic laws (Scala doesn't even require that flatMap have monadic shape: the chain after desugaring just has to typecheck); cf. duck-typing.
trait Trial[+Result] {
def result: Option[Result]
def flatMap[R >: Result](f: Unit => Trial[R]): Trial[R]
def map[R](f: Result => R): Trial[R]
}
case object NoSolution extends Trial[Nothing] {
def result = None
def flatMap[R](f: Unit => Trial[R]): Trial[R] = f(())
def map[R](f: Result => R): Trial[R] = this
}
case class Solution[Result](value: Result) extends Trial[Result] {
def result = Some(value)
def flatMap[R >: Result](f: Unit => Trial[R]): Trial[R] = this
def map[R](f: Result => R): Trial[R] = Solution(f(value))
}
scala> for {
| _ <- if (1 == 2) Solution("nope") else NoSolution
| _ <- NoSolution
| _ <- Solution("yay!")
| _ <- NoSolution
| x <- Solution("nope")
| } yield x
res0: Trial[String] = Solution(yay!)
scala> for {
| _ <- if (1 == 2) Solution("nope") else NoSolution
| _ <- NoSolution
| _ <- Solution("yay!")
| x <- NoSolution
| } yield x
res1: Trial[String] = Solution(yay!)
scala> for {
| _ <- if (1 == 2) Solution("nope") else NoSolution
| x <- NoSolution
| } yield x
res2: Trial[String] = NoSolution
Clearly, monadic laws are being violated: the only thing we could use for pure is Solution, but
scala> val f: Unit => Trial[Any] = { _ => NoSolution }
f: Unit => Trial[Any] = $Lambda$107382/0x00000008433be840#6c0e35d7
scala> Solution(5).flatMap(f)
res7: Trial[Any] = Solution(5)
scala> f(5)
<console>:13: warning: a pure expression does nothing in statement position
f(5)
^
res8: Trial[Any] = NoSolution
Absent Scala's willingness to convert any pure value to Unit, that wouldn't even type check, but still, it breaks left identity.

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

Running sequential Futures with similar shapes using for comprehension

def func1(list : List[T]) : Future[\/[Throwable,Unit] ]
def func2(list : List[T]) : Future[List[\/[Throwable,Unit]]]
where T is just a specific type and that type will be same for both the functions. Now func2 is dependent on success of first func 's future. so func2 should run sequentially only after func completed successfully. I want a for comprehension something in a similar line as below (following isn't valid compilable code) and return Future[\/[Throwable,Unit] ]
def func3 combiner(list) : Future[\/[Throwable,Unit] ] = for{
u <- func1(list)
us <- u
d <- func2(list)
}yield
Any pointers how to go about this?
Because futures either complete with a value or an exception you won't need the Either (or do you have other reasons for using it?).
Running this code should help you (and reading the documentation on futures):
import scala.concurrent._
import ExecutionContext.Implicits.global
def f1(l: List[Int]): Future[Int] = future { println("f1"); l head }
def f2(l: List[Int]): Future[Int] = future { println("f2"); throw new Exception("bang") }
def f3(l: List[Int]): Future[Int] = future { println("f3"); l last }
val result1 = for {
x1 <- f2(List(1, 2))
x2 <- f1(List(1, 2)) // f1 is not run
} yield x2
val result2 = for {
x1 <- f1(List(1, 2))
x3 <- f3(List(1, 2))
} yield x3
result1.onComplete(res => println("result1 = " + res))
result2.onComplete(res => println("result2 = " + res))

Quick failure when using for-comprehension with scala.util.Try

I really like scala.util.Try in Scala 2.10, and how it works with for-comprehension makes handling multiple steps that could go wrong easily.
For example, we could use the following code to make sure we only print out that two numbers if and only if everything is under control and we get value correctly.
def tryA: Try[Int] = {....}
def tryB: Try[Int] = {....}
for {
a <- tryA
b <- tryB
} {
println (s"We got:${a+b}")
}
But one of my concern is that this code is actually ignore any exceptions, which means it will looks like the following try-cactch block:
try {
// .....
} catch {
case _: Exception => // Swallow any exception
}
As far as I know, there is an argument that this kind of codes is a bad smell, because no one will notice there is an exception occurs.
What I would like to achieve is that still using for to make sure the println only execute if everything is OK, but if there is any exception in any steps, it will blow up and throw out the exception directly.
Currently here is how I do this, but it seems less elegant because it introduce a new Try[Unit] object, so I'm wondering how could I make this code better?
For example, is it possible to get rid of the result variable and result.get statement, but still get exception being thrown?
def tryA: Try[Int] = {....}
def tryB: Try[Int] = {....}
val result = for {
a <- tryA
b <- tryB
} yield {
println (s"We got:${a+b}")
}
result.get
Update
To make thing more clear, it is the result from Scala REPL of the first code in this question.
scala> def tryA: Try[Int] = Success(1)
tryA: scala.util.Try[Int]
scala> def tryB: Try[Int] = Failure(new Exception("error"))
tryB: scala.util.Try[Int]
scala> for {
| a <- tryA
| b <- tryB
| } {
| println (s"We got:${a+b}")
| }
scala>
We can see that nothing happens here, even tryB is a Failure with exception. What I would like to get is an exception being thrown, and without the introduce the new Try[Unit] object with yield, is this possible?
You can use recover:
import scala.util.Try
def tryEven = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i }
def tryEvenOrNeg1 = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i } recover { case exx: Exception => -1 }
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res1: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res2: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res3: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
Got 542, -1
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res5: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res6: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)
scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
Got 692, 750
I removed the resNN that reflected Success(()).
OK, I forgot we always has implicit conversion in Scala. ;-)
So we could implement this behaviour ourself, it will create more object than the yield version, but I think the intention of this code is much more clear.
implicit class BlowUpTry[T](current: Try[T]) {
def throwIfFailed: Try[T] = current match {
case Success(value) => current
case Failure(exception) => throw exception
}
}
def tryA: Try[Int] = Success(1)
def tryB: Try[Int] = Failure(new Exception("error"))
for {
a <- tryA.throwIfFailed
b <- tryB.throwIfFailed
} {
println(s"We got ${a + b}")
}

scala Either.RightProjection confusion (for comprehension de-sugaring)

I can use an = in a scala for-comprehension (as specified in section 6.19 of the SLS) as follows:
Option
Suppose I have some function String => Option[Int]:
scala> def intOpt(s: String) = try { Some(s.toInt) } catch { case _ => None }
intOpt: (s: String)Option[Int]
Then I can use it thus
scala> for {
| str <- Option("1")
| i <- intOpt(str)
| val j = i + 10 //Note use of = in generator
| }
| yield j
res18: Option[Int] = Some(11)
It was my understanding that this was essentially equivalent to:
scala> Option("1") flatMap { str => intOpt(str) } map { i => i + 10 } map { j => j }
res19: Option[Int] = Some(11)
That is, the embedded generator was a way of injecting a map into a sequence of flatMap calls. So far so good.
Either.RightProjection
What I actually want to do: use a similar for-comprehension as the previous example using the Either monad.
However, if we use it in a similar chain, but this time using the Either.RightProjection monad/functor, it doesn't work:
scala> def intEither(s: String): Either[Throwable, Int] =
| try { Right(s.toInt) } catch { case x => Left(x) }
intEither: (s: String)Either[Throwable,Int]
Then use:
scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str).right //note the "right" projection is used
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Product with Serializable with Either[java.lang.Throwable,(Int, Int)]
i <- intEither(str).right
^
The issue has something to do with the function that a right-projection expects as an argument to its flatMap method (i.e. it expects an R => Either[L, R]). But modifying to not call right on the second generator, it still won't compile.
scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str) // no "right" projection
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Either[Throwable,Int]
i <- intEither(str)
^
Mega-Confusion
But now I get doubly confused. The following works just fine:
scala> for {
| x <- Right[Throwable, String]("1").right
| y <- Right[Throwable, String](x).right //note the "right" here
| } yield y.toInt
res39: Either[Throwable,Int] = Right(1)
But this does not:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
<console>:14: error: type mismatch;
found : Either.RightProjection[Throwable,String]
required: Either[?,?]
Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
^
I thought these were equivalent
What is going on?
How can I embed an = generator in a for comprehension across an Either?
The fact that you cannot embed the = in the for-comprehension is related to this issue reported by Jason Zaugg; the solution is to Right-bias Either (or create a new data type isomorphic to it).
For your mega-confusion, you expanded the for sugar incorrectly. The desugaring of
for {
b <- x(a)
c <- y(b)
} yield z(c)
is
x(a) flatMap { b =>
y(b) map { c =>
z(c) }}
and not
x(a) flatMap { b => y(b)} map { c => z(c) }
Hence you should have done this:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right map { y => y.toInt } }
res49: Either[Throwable,Int] = Right(1)
More fun about desugaring (the `j = i + 10` issue)
for {
b <- x(a)
c <- y(b)
x1 = f1(b)
x2 = f2(b, x1)
...
xn = fn(.....)
d <- z(c, xn)
} yield w(d)
is desugared into
x(a) flatMap { b =>
y(b) map { c =>
x1 = ..
...
xn = ..
(c, x1, .., xn)
} flatMap { (_c1, _x1, .., _xn) =>
z(_c1, _xn) map w }}
So in your case, y(b) has result type Either which doesn't have map defined.