Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 940
Brian Hsu Avatar asked Feb 22 '13 01:02

Brian Hsu


People also ask

How to handle error in Scala?

You can use whatever you like, but Try/Success/Failure is generally used when dealing with code that can throw exceptions — because you almost always want to understand the exception — and Option/Some/None is used in other places, such as to avoid using null values.

How do you catch exceptions in Scala?

The try/catch construct is different in Scala than in Java, try/catch in Scala is an expression. The exception in Scala and that results in a value can be pattern matched in the catch block instead of providing a separate catch clause for each different exception. Because try/catch in Scala is an expression.

What is for comprehension in Scala?

Scala offers a lightweight notation for expressing sequence comprehensions. Comprehensions have the form for (enumerators) yield e , where enumerators refers to a semicolon-separated list of enumerators. An enumerator is either a generator which introduces new variables, or it is a filter.

What is try in Scala?

The Try type represents a computation that may either result in an exception, or return a successfully computed value. It's similar to, but semantically different from the scala. util. Either type. Instances of Try[T] , are either an instance of scala.


1 Answers

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(()).

like image 64
Randall Schulz Avatar answered Nov 12 '22 06:11

Randall Schulz