I'd like to await a scala future that may have failed. If I use Await.result the exception will be thrown. Instead, if I have f: Future[String] I would like a method Await.resultOpt(f): Option[String] or Await.resultEither(f): Either[String].
I could get this by using scala.util.control.Exception.catching or I could f map (Right(_)) recover { case t: Throwable => Left(t) }, but there must be a more straightforward way.
You could use Await.ready which simply blocks until the Future has either succeeded or failed, then returns a reference back to that Future.
From there, you would probably want to get the Future's value, which is an Option[Try[T]]. Due to the Await.ready call, it should be safe to assume that the value is a Some. Then it's just a matter of mapping between a Try[T] and an Either[Throwable, T].
The short version:
val f: Future[T] = ...
val result: Try[T] = Await.ready(f, Duration.Inf).value.get
val resultEither = result match {
case Success(t) => Right(t)
case Failure(e) => Left(e)
}
The shorter version, just to promote the API:
scala> val f = Future(7)
f: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise#13965637
scala> f.value.get
res0: scala.util.Try[Int] = Success(7)
scala> import scala.util._
import scala.util._
scala> Either.cond(res0.isSuccess, res0.get, res0.failed.get)
res2: scala.util.Either[Throwable,Int] = Right(7)
scala> val f = Future[Int](???)
f: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise#64c4c1
scala> val v = f.value.get
v: scala.util.Try[Int] = Failure(java.util.concurrent.ExecutionException: Boxed Error)
scala> Either.cond(v.isSuccess, v.get, v.failed.get)
res4: scala.util.Either[Throwable,Int] = Left(java.util.concurrent.ExecutionException: Boxed Error)
It has a slight advantage in being a one-liner.
But of course, after adding a .toEither extension method, you don't care how many lines it took.
You could start to make your own type utils and do something like so
trait RichTypes {
import scala.util.{Try, Success, Failure}
import scala.concurrent.{Await, Future}
import scala.concurrent.duration.Duration
implicit class RichFuture[T](f: Future[T]) {
def awaitResult(d: Duration): Either[Throwable, T] = {
Try(Await.result(f, d)).toEither
}
}
implicit class RichTry[T](tri: Try[T]) {
def toEither(): Either[Throwable, T] = {
tri.fold[Either[Throwable, T]](Left(_), Right(_))
}
}
}
object Example
extends App
with RichTypes {
import scala.concurrent.Future
import scala.concurrent.duration._
val succ = Future.successful("hi").awaitResult(5.seconds)
val fail = Future.failed(new Exception("x")).awaitResult(5.seconds)
println(succ) // Right(hi)
println(fail) // Left(Exception(x))
}
I separated it out for a Try to also have a .fold :).
Related
That may be a dumb question, but starting with ZIO, I cannot manage to convert a Seq[ZIO] to ZIO[Seq]:
def translate(keys: Seq[String], locales: Seq[Locale]):RIO[Translator, Seq[Translation]] = {
for {
service <- ZIO.environment[Translator]
} yield {
// service.translate produce a zio.Task[Translation]
keys.map(k => service.translate(k, locales)
}
}
Required: RIO[Translator, Seq[Translation]]
Found : ZIO[Translator, Nothing, Seq[zio.Task[Translation]]
I tried flatMap, flatten, collectAll and merge but I was not able to get the expected result with anyone.
How can I transform a Seq[ZIO[_, _, B]] to a ZIO[_, _, Seq[B]] ?
Thanks
Edit: It seems that ZIO.foreach is the best option, however I still have it wrapped inside another ZIO due to the for comprehension.
Because for loops translate to flatMap except for the last line which is a map, you want to add the foreach call within the for-loop.
def translate(keys: Seq[String], locales: Seq[Locale]): RIO[Translator, Seq[Translation]] = {
for {
translator <- ZIO.environment[Translator]
translations <- ZIO.foreach(keys)(translator.translate(_, locales))
} yield translations
}
If I got you right you can do it using traverse function from cats:
import cats.instances.list._
import cats.syntax.traverse._
import zio.{RIO, Task, ZIO}
import zio.interop.catz._
import java.util.Locale
case class Translation()
trait Translator {
def translate(k: String, locales: Seq[Locale]): Task[Translation]
}
def translate(keys: Seq[String], locales: Seq[Locale]): RIO[Translator, Seq[Translation]] = {
val translator: Translator = ???
for {
service <- ZIO.effect(translator)
result <- keys.toList.traverse(k => service.translate(k, locales))
} yield result
}
For map List[ZIO[_, _, B]] to ZIO[_, _, List[B]] you can use sequence function and I would advice to use cats library for that.
import zio.ZIO
import zio.interop.catz._
import cats.syntax.traverse._
import cats.instances.list._
def ziosSequence[B](seqZIO: Seq[ZIO[Any, Throwable, B]]): ZIO[Any, Throwable, Seq[B]] =
seqZIO.toList.sequence.map(_.toSeq)
the sequence signature is:
def sequence[G[_]: Applicative, A](fga: F[G[A]]): G[F[A]] =
traverse(fga)(ga => ga)
Here we see what function do what we need.
it requires Applicative instance for G (G is ZIO in your case), and we just import it using import zio.interop.catz._
Also, to make list is able to call sequence we need import Traverse instance for List:
by import cats.instances.list._
Unfortunetaly we can not do the same tricks with Seq because we need Traverse instance for sequence, and we should convert Seq to List back and forth before and after sequence.
useful links:
typelevel documentation cats.Traverse
typelevel documentation cats.Applicative
To "exchange" List and ZIO you could dance this way:
def dance(x: List[ZIO[Any,Throwable,Int]]): ZIO[Any, Throwable, List[Int]] =
x.map ( a => a.map(x=> List(x)))
.fold ( ZIO.succeed( List[Int]()) )
((x, y) => x.map(a => y.map(b => a ++ b ) )
.flatten
)
I have this code:
type Response[A] = EitherT[Future, String, A]
val powerLevels = Map(
"Jazz" -> 6,
"Bumblebee" -> 8,
"Hot Rod" -> 10
)
def getPowerLevel(autobot: String): Response[Int] = {
val result = Future {
powerLevels.get(autobot) {
case Some(number) => Right(number)
case None => Left(s"Can't get connect to $autobot")
}
}
}
I can't understand how I can convert result of calculation in function getPowerLevel (Future[Either[String, Int]]) to (Writer correctly to Response[Int] type. I'd like to do calling powerLevels.get(autobot) in Future.
As #Luis pointed out, all you need to use is EitherT.apply:
import cats.data.EitherT
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import cats.implicits._
type Response[A] = EitherT[Future, String, A]
val powerLevels = Map(
"Jazz" -> 6,
"Bumblebee" -> 8,
"Hot Rod" -> 10
)
def getPowerLevel(autobot: String): Response[Int] = {
val result = Future {
powerLevels.get(autobot) match {
case Some(number) => Right(number)
case None => Left(s"Can't get connect to $autobot")
}
}
EitherT(result)
}
Monad transformers take stackable monads to return a composable monad. For example, in this case EitherT[Future, String, A] will take Future[Either[String, A]] to return a monad that is composable.
Although other solutions aptly satisfy this requirement, we can make use of cond API from Either to write it more succinctly like so:
def getPowerLevel(autobot: String): Response[Int] = {
val powerLevel = powerLevels.get(autobot)
EitherT(Future(Either.cond(powerLevel.isDefined, powerLevel.get, s"$autobot unreachable")))
}
There are some suggestions on StackOverflow on how to handle a list of Futures but I want to try my own approach. But I am unable to compile the following code
I have a list of Futures.
I want to count how many of them passed or failed. I should get (2,1)
I store this in a tuple
The approach I want to take is go through each element of the list. The element of the list is Future[Int]. for each element, I call flatMap which calls the next cycle of recursion (I am assuming that if flatMap gets called then that particular future would have been successful so I increment pass count). Similarly, I want to call next cycle of recursion in recover and increment fail count but I am getting compilation error in it.
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success, Try}
import scala.concurrent.duration._
import scala.language.postfixOps
object ConcurrencyExample extends App {
type pass = Int
type fail = Int
val time = System.currentTimeMillis()
//use recursion to process each Future in the list
def segregate(l:List[Future[Int]]):Future[Tuple2[pass,fail]] = {
def go(l:List[Future[Int]],t:Tuple2[pass,fail]):Future[Tuple2[pass,fail]] = {
l match {
case Nil => Future{t}
//l is List of Future[Int]. flatMap each successful Future
//recover each failed Future
case l::ls => {
l flatMap (x => go(ls, (t._1 + 1, t._2)))
**l.recover({ case e => go(ls, (t._1 + 1, t._2))})**//I get error here
}
}
}
go(l,(0,0))
}
//hardcoded future
val futures2: List[Future[Int]] = List(Future {
1
}, Future {
2
}, Future {
throw new Exception("error")
})
val result = segregate(futures2)
result onComplete {
case Success(v) => println("pp:" + v)
case Failure(v) => println("fp:" + v)
}
Await.result(result,1000 millis)
}
#evan058 is correct about the signature of recover. But you can fix your program by changing recover to recoverWith.
recoverWith is to recover as flatMap is to map.
Here's the complete solution (with minor stylistic improvements):
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success, Try}
import scala.concurrent.duration._
import scala.language.postfixOps
object ConcurrencyExample extends App {
type pass = Int
type fail = Int
val time = System.currentTimeMillis()
//use recursion to process each Future in the list
def segregate[T](fs:List[Future[T]]):Future[(pass,fail)] = {
def go(fs:List[Future[T]],r:Future[(pass,fail)]):Future[(pass,fail)] = fs match {
case Nil => r
case l::ls =>
val fx = l.transform({_ => (1, 0)}, identity).recoverWith[(pass,fail)]({case _: Exception => Future(0, 1) })
for (x <- fx; t <- r; g <- go(ls, Future(t._1+x._1,t._2+x._2))) yield g
}
go(fs,Future((0,0)))
}
//hardcoded future
val futures2 = List(Future(1), Future(2), Future(throw new Exception("error")))
val result = segregate(futures2)
result onComplete {
case Success(v) => println(s"successes: ${v._1}, failures: ${v._2}")
case Failure(v) => v.printStackTrace()
}
Await.result(result,1000 millis)
}
If you look at the docs, the signature of recover is:
def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U]
You are calling recover on l which is a Future[Int] so recover is expecting a U >: Int.
However you are calling go again which has the return type Future[(pass, fail)] which is not >: Int.
Let's say I have functions which return Future[Either[_, _] and I want to apply some of these functions in case of failures, that means apply them only to left side. The simplified example is:
def operation1: Future[Either[String, Int]] = Future.successful(Right(5))
def operation2: Future[Either[String, Int]] = Future.successful(Left("error"))
def operation2FallBackWork = Future.successful{
println("Doing some revert stuff")
Left("Error happened, but reverting was successful")
}
val res = for {
res1 <- EitherT.fromEither(operation1)
res2 <- EitherT.fromEither(operation2)//.leftFlatMap(operation2FallBackWork) -????
} yield res1 + res2
Await.result(res.toEither, 5 seconds)
How to achieve that?
The closest thing to a leftFlatMap is MonadError's handleError, which has exactly the signature you'd expect from something called leftFlatMap (although note that you'll need to change the fallback operation to an EitherT and provide a constant function instead of passing it as-is). You can use the EitherT instance directly like this:
import scala.concurrent.{ Await, Future }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scalaz._, Scalaz._
def operation1: Future[Either[String, Int]] = Future.successful(Right(5))
def operation2: Future[Either[String, Int]] = Future.successful(Left("error"))
def operation2FallBack: EitherT[Future, String, Int] = EitherT(
Future.successful {
println("Doing some revert stuff")
"Error happened, but reverting was successful".left
}
)
val E: MonadError[({ type L[x] = EitherT[Future, String, x] })#L, String] =
implicitly
val res = for {
a <- EitherT.fromEither(operation1)
b <- E.handleError(EitherT.fromEither(operation2))(_ => operation2FallBack)
} yield a + b
Await.result(res.toEither, 5.seconds)
You can also use the syntax provided by MonadError to make it look like EitherT has a handleError method, although it takes a bit more ceremony to get the Scala compiler to recognize that your operations have the right shape:
import scala.concurrent.{ Await, Future }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scalaz._, Scalaz._
type FE[x] = EitherT[Future, String, x]
def operation1: FE[Int] = EitherT(Future.successful(5.right))
def operation2: FE[Int] = EitherT(Future.successful("error".left))
def operation2FallBack: FE[Int] = EitherT(
Future.successful {
println("Doing some revert stuff")
"Error happened, but reverting was successful".left
}
)
val res = for {
a <- operation1
b <- operation2.handleError(_ => operation2FallBack)
} yield a + b
Await.result(res.toEither, 5.seconds)
I'd prefer this second version, but it's a matter of style and taste.
I'm trying to create some simple custom String interpolator, and I'm successful as long as I don't try to use a type parameter.
import scala.concurrent.Future
object StringImplicits {
implicit class FailureStringContext (val sc : StringContext) extends AnyVal {
// This WORKS, but it's specific to Future :(
def fail[T](args : Any*): Future[T] = {
val orig = sc.s (args : _*)
Future.exception[T](new Exception(orig))
}
// I want this to work for Option,Try,Future!!
def fail[M,T](args:Any*): M[T] = {
val orig = sc.s (args : _*)
// Obviously does not work..
M match {
case Future => Future.exception(new Exception(orig))
case Option => None
case Try => Failure(new Exception(orig))
case _ => ???
}
}
}
}
Can I get this to work? I can't use parametric polymorphism because I'm not the one defining those three types.
What's the equivalent in the type level for that pseudo-code pattern match?
LATEST ATTEMPT
My latest attempt was to use implicitly, but I don't have such implicit! I'd be actually interested to grab a hold of the type that the compiler wants me to return according to type inference.
def fail[T, M[T]](args:Any*): M[T] = {
val orig = sc.s(args: _*)
implicitly[M[T]] match {
case _:Future[T] => Future.exception(new Exception(orig))
case _ => ???
}
}
<console>:18: error: could not find implicit value for parameter e: M[T]
implicitly[M[T]] match {
^
<console>:19: error: value exception is not a member of object scala.concurrent.Future
case _: Future[T] => Future.exception(new Exception(orig))
^
In my opinion the simplest is to rely on good old overloading: just define a different overload for each type that you want to handle.
Now of course, there is the problem of having different overloads with the same signature, and as usual in scala, you can use tricks to work around them. Here we'll add dummy implicit parameters to force each overload to have a distinct signature. Not pretty but it works and will suffice in this case.
import scala.concurrent.Future
import scala.util.{Try, Failure}
implicit class FailureStringContext (val sc : StringContext) extends AnyVal {
def fail[T](args : Any*): Future[T] = {
Future.failed[T](new Exception(sc.s (args : _*)))
}
def fail[T](args : Any*)(implicit dummy: DummyImplicit): Option[T] = {
Option.empty[T]
}
def fail[T](args : Any*)(implicit dummy: DummyImplicit, dummy2: DummyImplicit): Try[T] = {
Failure[T](new Exception(sc.s (args : _*)))
}
}
And tada:
scala> fail"oops": Option[String]
res6: Option[String] = None
scala> fail"oops": Future[String]
res7: scala.concurrent.Future[String] = scala.concurrent.impl.Promise$KeptPromise#6fc1a8f6
scala> fail"oops": Try[String]
res8: scala.util.Try[String] = Failure(java.lang.Exception: oops)