1

I have a similar question to what is asked here (Multiple or Single Try Catch), however in my case I need to follow this pattern for functional (and not performance reasons)

Essentially I am handling parameter errors in Scalatra, and I need an easy way to catch if any conversions fail without the first failure skipping the rest of the try calls

In other words, I need something that follows a pattern like this

  def checkMultiple(a:Seq[Try]):Either[Map[_,Throwable],Map[_,Success]] = {
    ???
  }

I put in a Sequence of try clauses. Should any of them fail, it will return back a map of all of the failed try's , not just the first one (which will have a map of the try that failed, along with its exception), else it will return a map of all of the try's mapped with their success values

Does anyone know if there is a monadic pattern that already does this in essence, or if there is some util library which does this? Else how would you define the above function?

Community
  • 1
  • 1
mdedetrich
  • 1,899
  • 1
  • 18
  • 29
  • 1
    Sounds like you want to look at Scalaz' Validation monad. – Shadowlands Aug 29 '13 at 00:29
  • @Shadowlands Validation is not a monad, but it is an applicative functor which is all that is needed for this magic to happen. mdedetrich Like Shadowlands said, scalaz.Validation is what you want. – adelbertc Aug 29 '13 at 00:38
  • 3
    See [my answer here](http://stackoverflow.com/a/12309023/334519) for some discussion of `Validation[NonEmptyList[Throwable], _]`, and how it differs from the standard library's `Either` and `Try` in this respect. – Travis Brown Aug 29 '13 at 00:42
  • 1
    I ended up using this guide here, and it works perfectly for me http://eed3si9n.com/learning-scalaz/Validation.html – mdedetrich Aug 29 '13 at 02:17

1 Answers1

3

You can accomplish something much like this with plain Scala, though it's a little more work than with Scalaz' Validation.

def checkMultiple[A,B](data: Seq[A])(f: A => B): Either[Map[A,Throwable], Map[A,B]] = {
  val caught = data.map(a => a -> Try(f(a)))
  val wrong = caught.collect{ case (a, Failure(t)) => a -> t }
  if (!wrong.isEmpty) Left(wrong.toMap)
  else Right(caught.map(x => x._1 -> x._2.get).toMap)
}

Here it is at work:

scala> checkMultiple(Seq(1,2,3,4))(x => if (x>4) throw new Exception else x)
res1: scala.util.Either[Map[Int,Throwable],Map[Int,Int]] = 
  Right(Map(1 -> 1, 2 -> 2, 3 -> 3, 4 -> 4))

scala> checkMultiple(Seq(3,4,5,6))(x => if (x>4) throw new Exception else x)
res2: scala.util.Either[Map[Int,Throwable],Map[Int,Int]] = 
  Left(Map(5 -> java.lang.Exception, 6 -> java.lang.Exception))
Rex Kerr
  • 166,841
  • 26
  • 322
  • 407