0

How can an Iterable of Try values be converted into a Try of Iterable? If the Iterable contains a failure then the first Failure should be returned, if there are no failures then an Iterable of the Success values should be returned.

As an example, assume the conversion function is called squash:

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

def squash[U](iterable : Iterable[Try[U]]) : Try[Iterable[U]] = ???

The expected behavior for an Iterable containing failures:

val containsFailure : Iterable[Try[Int]] = 
  Iterable[Try[Int]](Success(42), Failure(new Exception("foo")), Success(43), Failure(new Exception("bar")))

assert(squash(containsFailure) ==  Failure(new Exception("foo")))

And the expected behavior for an Iterable with no failures:

val noFailures : Iterable[Try[Int]] = 
  Iterable[Try[Int]](Success(42), Success(43), Success(44))

assert(squash(noFailures) == Success(Iterable(42,43,44)))

I am looking for a solution that only contains standard scala functionality, please don't include libraries such as scalaz or cats as part of the answer.

Thank you in advance for your consideration and response.

Ramón J Romero y Vigil
  • 17,373
  • 7
  • 77
  • 125
  • Are you open to using external libraries and / or to change your types? – Luis Miguel Mejía Suárez Jan 19 '21 at 17:14
  • @LuisMiguelMejíaSuárez No external libraries, scala native functionality only. I'll update my question accordingly. – Ramón J Romero y Vigil Jan 19 '21 at 17:14
  • Ok, and are you open to using something that is not an **Iterable**? Technically speaking an **Iterable** doesn't even guarantee order. – Luis Miguel Mejía Suárez Jan 19 '21 at 17:22
  • @LuisMiguelMejíaSuárez Ah, yes that does make sense. But it should be kept "generic" if possible, e.g. `Traversable` instead of `Array` or `List`. – Ramón J Romero y Vigil Jan 19 '21 at 17:25
  • When you say generic is because you want to preserve the collection type? Like if I pass a **List** return a **List**? - Most of the _"abstract"_ collections of the stdlib are there just for code reuse not really for end users, most of them are pretty useless IMHO and many _(like me)_ argue the stdlib could be simplified a lot but that is another discussion. – Luis Miguel Mejía Suárez Jan 19 '21 at 17:26
  • No, not necessarily for type preservation. Just to keep the resulting type independent of underlying implementation details. – Ramón J Romero y Vigil Jan 19 '21 at 17:29
  • 2
    [List\[Try\[T\]\] to Try\[List\[T\]\] in Scala](https://stackoverflow.com/q/57516234/2359227) – Tomer Shetah Jan 19 '21 at 17:31
  • 1
    [Converting a List\[Try\[A\]\] to List\[A\] in Scala](https://stackoverflow.com/q/27955294/2359227) – Tomer Shetah Jan 19 '21 at 17:32
  • 1
    Here are a couple of alternatives: https://scastie.scala-lang.org/BalmungSan/CD9okV0fTBeKmt3JV6DnSQ/14 - BTW, while I agree it is not good to include something like **cats** for just one function, at one point one has to consider how many things are you reimplementing. – Luis Miguel Mejía Suárez Jan 19 '21 at 17:48

2 Answers2

0

A solution that returns a Seq instead of an Iterable:

def squash[U](iterable : Iterable[Try[U]]): Try[Seq[U]] =
  iterable.foldLeft[Try[Vector[U]]](Success(Vector.empty[U]))((result: Try[Vector[U]], t: Try[U]) =>
    result flatMap (vector => t match {
      case Failure(ex) => Failure[Vector[U]](ex)
      case Success(value) => Success(vector :+ value)
    }))
Ramón J Romero y Vigil
  • 17,373
  • 7
  • 77
  • 125
0

Keeping with the profile as posted in the question.

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

def squash[U](iterable : Iterable[Try[U]]) : Try[Iterable[U]] =
  iterable.foldLeft(Try(Iterable.empty[U])){
    case (Success(a),Success(b)) => Success(a ++ Iterable(b))
    case (Success(_),Failure(ex)) => Failure(ex)
    case (failed, _) => failed
  }
jwvh
  • 50,871
  • 7
  • 38
  • 64