Brian Hsu
Brian Hsu

Reputation: 8821

Quick failure when using for-comprehension with scala.util.Try

I really like scala.util.Try in Scala 2.10, and how it works with for-comprehension makes handling multiple steps that could go wrong easily.

For example, we could use the following code to make sure we only print out that two numbers if and only if everything is under control and we get value correctly.

def tryA: Try[Int] = {....}
def tryB: Try[Int] = {....}

for {
  a <- tryA
  b <- tryB
} { 
  println (s"We got:${a+b}")
}

But one of my concern is that this code is actually ignore any exceptions, which means it will looks like the following try-cactch block:

try {
  // .....
} catch {
  case _: Exception => // Swallow any exception
}

As far as I know, there is an argument that this kind of codes is a bad smell, because no one will notice there is an exception occurs.

What I would like to achieve is that still using for to make sure the println only execute if everything is OK, but if there is any exception in any steps, it will blow up and throw out the exception directly.

Currently here is how I do this, but it seems less elegant because it introduce a new Try[Unit] object, so I'm wondering how could I make this code better?

For example, is it possible to get rid of the result variable and result.get statement, but still get exception being thrown?

def tryA: Try[Int] = {....}
def tryB: Try[Int] = {....}

val result = for {
  a <- tryA
  b <- tryB
} yield { 
  println (s"We got:${a+b}")
}
result.get

Update

To make thing more clear, it is the result from Scala REPL of the first code in this question.

scala> def tryA: Try[Int] = Success(1)
tryA: scala.util.Try[Int]

scala> def tryB: Try[Int] = Failure(new Exception("error"))
tryB: scala.util.Try[Int]

scala> for {
     |   a <- tryA
     |   b <- tryB
     | } { 
     |   println (s"We got:${a+b}")
     | }

scala> 

We can see that nothing happens here, even tryB is a Failure with exception. What I would like to get is an exception being thrown, and without the introduce the new Try[Unit] object with yield, is this possible?

Upvotes: 8

Views: 6788

Answers (2)

Brian Hsu
Brian Hsu

Reputation: 8821

OK, I forgot we always has implicit conversion in Scala. ;-)

So we could implement this behaviour ourself, it will create more object than the yield version, but I think the intention of this code is much more clear.

implicit class BlowUpTry[T](current: Try[T])  {

  def throwIfFailed: Try[T] = current match {
    case Success(value)     => current
    case Failure(exception) => throw exception
  }

}

def tryA: Try[Int] = Success(1)
def tryB: Try[Int] = Failure(new Exception("error"))

for {
  a <- tryA.throwIfFailed
  b <- tryB.throwIfFailed
} {
  println(s"We got ${a + b}")
}

Upvotes: 0

Randall Schulz
Randall Schulz

Reputation: 26486

You can use recover:

import scala.util.Try

def tryEven = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i }

def tryEvenOrNeg1 = Try { val i = (math.random * 1000).toInt; if (i % 2 != 0) throw new Exception("odd") else i } recover { case exx: Exception => -1 }

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res1: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res2: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res3: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
Got 542, -1

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res5: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
res6: scala.util.Try[Unit] = Failure(java.lang.Exception: odd)

scala> for (a <- tryEven; b <- tryEvenOrNeg1) yield println(s"Got $a, $b")
Got 692, 750

I removed the resNN that reflected Success(()).

Upvotes: 4

Related Questions