8

I would like to know how to convert a List[Try[T]] into Try[List[T]] in Scala?

I have tried using an accumulator and folding right but it doesn't seem ideal.

Tomer Shetah
  • 8,413
  • 7
  • 27
  • 35
Valahu
  • 763
  • 2
  • 8
  • 15

4 Answers4

13

Using cats it's as easy as:

import cats._
import cats.data._
import cats.implicits._
import scala.util.{Try, Success, Failure}

val tries: List[Try[Int]] = List(Success(1), Success(2), Success(3))
tries.sequence

More information in the Traverse docs.

Karl Bielefeldt
  • 47,314
  • 10
  • 60
  • 94
7
Try(list.map(_.get))

This will return only the first failure, so you need something more complicated if you want to catch all the failures in the list.

Tim
  • 26,753
  • 2
  • 16
  • 29
  • See my answer to [this question](https://stackoverflow.com/questions/58074846/how-to-transform-a-seqtry-to-a-tryseq) for the code that catches multiple failures. – Tim Sep 25 '19 at 08:12
4

I would also recommend just using Cats...

But, if you do not want to add another (big) dependency to your project, just for one function.
You can implement it your self! - (cats implementation may be better)

import scala.util.{Try, Success, Failure}

def sequence[A](list: List[Try[A]]): Try[List[A]] = {
  @annotation.tailrec
  def loop(remaining: List[Try[A]], acc: List[A]): Try[List[A]] =
    remaining match {
      case Nil                => Success(acc.reverse)
      case Success(a) :: tail => loop(remaining = tail, acc = a :: acc)
      case Failure(e) :: _    => Failure(e)
    }
  loop(remaining = list, acc = List.empty)
}

Also, if you may use traverse instead of sequence if you did a map just before.

def traverse[A, B](list: List[A])(f: A => Try[B]): Try[List[B]] = {
  @annotation.tailrec
  def loop(remaining: List[A], acc: List[B]): Try[List[B]] =
    remaining match {
      case Nil          => Success(acc.reverse)
      case head :: tail => f(head) match {
        case Success(b) => loop(remaining = tail, acc = b :: acc)
        case Failure(e) => Failure(e)
      }
    }
  loop(remaining = list, acc = List.empty)
}

Anyways, Cats (and FP in general) is very useful (as you have just seen).
Thus, I would recommend you to give it a try.

  • Nice solution. I suppose method `traverse` should take a `List[Try[A]]` rather than `List[A]`, consequently `f(head) match {...}` should be something like `head.flatMap(f) match {...}`. – Leo C Aug 16 '19 at 01:02
  • 2
    @LeoC No, [**traverse**](https://typelevel.org/cats/api/cats/Traverse.html#traverse[G[_],A,B](fa:F[A])(f:A=%3EG[B])(implicitevidence$1:cats.Applicative[G]):G[F[B]]) is defined as `traverse[G[_] : Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]` - Thus, in this specific case, the idea is to have a list of plain values and and a function that will transform these into **Try**-effectual values. Remember that `col.map(f).sequence === col.traverse(f)`, that is why I said that you can use it if you did a **map** just before. – Luis Miguel Mejía Suárez Aug 16 '19 at 01:06
2

Cats is a nice way to go but it can be done via the Standard Library without too much complication.

import util.{Try, Success, Failure}

def seqnc[T](lst :List[Try[T]]) :Try[List[T]] =
  lst.foldRight(Try(List.empty[T])) {
    case (tt, acc) => for { t <- tt
                            a <- acc
                          } yield t :: a
  }
jwvh
  • 50,871
  • 7
  • 38
  • 64