How to transform a Seq[Try] to a Try[Seq] - scala

With Futures there is an easy way to transform Seq[Future] to a Future[Seq]:
Future.sequence(seqOfFutures)
I could not find an analog thing with Try.
It works with foldLeft but what I really like would have something like Try.sequence(seqOfTry).
Is there a reason that such a function is not provided?
How is this done properly?
Semantics:
A List of the values on Success: Success(Seq(1,2,3,4))
For Failure there are 2 possibilities:
Fails on the fist Failure and returns it. This is handled by this question: listtryt-to-trylistt-in-scala
Gathers all Failures and returns a 'compound' Failure.
Is there also a solution for the 'compound' Failure?

As per Luis' suggestion Validated is designed for error accumulation so consider traverse like so
la.traverse(_.toEither.toValidatedNec)
lb.traverse(_.toEither.toValidatedNec)
which outputs
res2: cats.data.ValidatedNec[Throwable,List[Int]] = Invalid(Chain(java.lang.RuntimeException: boom, java.lang.RuntimeException: crash))
res3: cats.data.ValidatedNec[Throwable,List[Int]] = Valid(List(1, 2, 3))
where
import cats.syntax.traverse._
import cats.instances.list._
import cats.syntax.either._
import scala.util.{Failure, Success, Try}
val la: List[Try[Int]] = List(Success(1), Success(2), Failure(new RuntimeException("boom")), Success(3), Failure(new RuntimeException("crash")))
val lb: List[Try[Int]] = List(Success(1), Success(2), Success(3))
Without error accumulation we could just sequence like so
import cats.implicits._
la.sequence
which outputs
res0: scala.util.Try[List[Int]] = Failure(java.lang.RuntimeException: boom)

This is a solution to the second question.
case class CompoundError(errs: List[Throwable]) extends Throwable
def toTry[T](list: List[Try[T]]): Try[List[T]] =
list.partition(_.isSuccess) match {
case (res, Nil) =>
Success(res.map(_.get))
case (_, errs) =>
Failure(CompoundError(errs.collect { case Failure(e) => e }))
}
The partition operation separates the successes and failures, and the match returns the appropriate value depending on whether there are any failures or not.
Previous solution:
case class CompoundError(errs: List[Throwable]) extends Throwable
def toTry[T](list: List[Try[T]]): Try[List[T]] = {
val (res, errs) = list.foldLeft((List.empty[T], List.empty[Throwable])) {
case ((res, errs), item) =>
item match {
case Success(t) => (t :: res, errs)
case Failure(e) => (res, e :: errs)
}
}
errs match {
case Nil => Success(res.reverse)
case _ => Failure(CompoundError(errs.reverse))
}
}

Related

How to use transformWith() to ignore failed futures?

I need to process sequence of transformations into one transformation, in order to if I have some failed Future it should be just ignored(I am trying to do it without recover or recoverWith, but smth going wrong, code fails when meet any failure)
type Transformation[T] = T => Future[T]
//in - Seq(trans1, trans2, trans3)
in.reduce[Transformation[T]](
(acc, el) =>
acc.andThen[Future[T]](
ft =>
ft.flatMap(el)
.transformWith[T](
t =>
t match {
case Failure(exception) => ft //this line is the most suspicious for me
case Success(value) => Future.successful(value)
}
)
)
)
The transformWith doesn't seem to provide you the possibility to recover with the original input value from the previous step, because it has to work with a Try. What is it supposed to do if the Try turns out to be a failure? By that time, it does not have the original input to fall back to, it only has a Throwable, not a T. So, transformWith seems insufficient.
If the pipeline is just a few transformations long, you can try foldLeft with fallbackTo:
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
type Transformation[T] = T => Future[T]
def chainTrafos[T](
trafos: List[Transformation[T]],
value: T
): Future[T] = {
trafos.foldLeft(Future { value })(
(f, t) => f.flatMap(x => t(x).fallbackTo(Future { x }))
)
}
val ta: Transformation[Int] = x => Future { x * x }
val tb: Transformation[Int] = x => Future.failed(new Error("oops"))
val tc: Transformation[Int] = x => Future { x - 58 }
println(Await.result(chainTrafos(List(ta, tb, tc), 10), 10.seconds))
prints
42

Future does not compile with Success and Failure

Why this does not compile? I get the following error in the Success and Failure lines:
constructor cannot be instantiated to expected type; found :
scala.util.Success[T] required: Int
And the code:
val future = Future { 1 }
future.map {
case Success(s) => s
case Failure(f) => 0
}
Because map of a Future[Int] expects a function with domain Int, but instead you are trying to pass some function with domain Try[Int] into it.
The syntactically closest thing that works is onComplete:
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Try, Success, Failure}
val future = Future { 1 }
future.onComplete {
case Success(s) => s
case Failure(f) => 0
}
However, this is probably not what you want, because the expressions s and 0 don't do anything, they are therefore not very interesting callbacks. Instead, you probably either want to transform the result:
future.transform {
case Success(s) => Success(s)
case Failure(f) => Success(0)
}
or recover from the failure with a 0:
future.recover {
case f: Exception => 0
}
Are you trying to do something like:
val future = Future {
1
}
future onComplete {
case Success(s) => s
case Failure(f) => 0
}

unable to compile a code with list of Future

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.

Scala Try - Should this not result in a Failure?

import scala.collection.JavaConverters._
val line: List[String] = null
val myTry = Try(line.asJava)
val result = myTry match {
case Success(_) => "Success"
case Failure(_) => "Failure"
}
println(result)
This code snippet prints "Success". If I try to access myTry.get, then it throws a NullPointerException.
From how I understand Try, shouldn't myTry be a Failure?
From how I understand Try, shouldn't myTry be a Failure?
Specifically, asJava on a List creates a wrapper around the original collection in the form of a SeqWrapper. It doesn't iterate the original collection:
case class SeqWrapper[A](underlying: Seq[A]) extends ju.AbstractList[A] with IterableWrapperTrait[A] {
def get(i: Int) = underlying(i)
}
If you use anything else that does iterate the collection or tries to access it, like toSeq, you'll see the failure:
import scala.collection.JavaConverters._
val line: List[String] = null
val myTry = Try(line.toSeq)
val result = myTry match {
case Success(_) => "Success"
case Failure(_) => "Failure"
}
println(result)

How to know which Future fail when doing Future.sequence?

Here is an example receive part of an actor I'm working on:
def receive = {
case "begin" =>
val listOfFutures: IndexedSeq[Future[Any]] = workers.map(worker => worker ? Work("test"))
val future: Future[IndexedSeq[Any]] = Future.sequence(listOfFutures)
future onComplete {
case Success(result) => println("Eventual result: "+result)
case Failure(ex) => println("Failure: "+ex.getMessage)
}
case msg => println("A message received: "+msg)
}
When ask fails for one of the workers (in case of a timeout), sequence future completes with failure. However I want to know which worker(s) have failed. Is there a more elegant way rather than simply mapping listOfFutures one by one without using Future.sequence ?
You can use the future's recover method to map or wrap the underlying exception:
import scala.concurrent.{Future, ExecutionContext}
case class WorkerFailed(name: String, cause: Throwable)
extends Exception(s"$name - ${cause.getMessage}", cause)
def mark[A](name: String, f: Future[A]): Future[A] = f.recover {
case ex => throw WorkerFailed(name, ex)
}
import ExecutionContext.Implicits.global
val f = (0 to 10).map(i => mark(s"i = $i", Future { i / i }))
val g = Future.sequence(f)
g.value // WorkerFailed: i = 0 - / by zero
Thanks to #O__ I have come with another solution that may a better fit some some cases.
case class WorkerDone(name: String)
case class WorkerFailed(name: String)
import ExecutionContext.Implicits.global
val f = (0 to 10).map {
i => Future {i/i; WorkerDone(s"worker$i")}.recover{
case ex => WorkerFailed(s"worker$i")
}
}
val futureSeq = Future.sequence(f)
futureSeq onComplete {
case Success(list) => list.collect {case result:WorkerFailed => result}.foreach {failed => println("Failed: "+failed.name)}
case Failure(ex) => println("Exception: "+ex.getMessage)
}
// just to make sure program doesn't end before onComplete is called.
Thread.sleep(2000L)
I'm not sure that if my example is a good practice, but my aim is to know which workers did fail no matter how did they fail.