8

Is there a more elegant way of doing this in scala?

def doTheDangerousThing(): Try[Result] = { val result = Try(dangerousOp) if (result.isFailure) { println("error") } result } 

8 Answers 8

19

I think your if statement is perfectly valid. Here is another alternative:

def doTheDangerousThing(): Try[Result] = Try(dangerousOp) recoverWith { case exception => println("error"); Failure(exception) } 
Sign up to request clarification or add additional context in comments.

2 Comments

Though valid (mine, that is), it looks a bit imperative. It would be nice to have a variant of recoverWith just for side-effecting, and not having to return Failure again.
@PabloFernandez agreed. I guess a foreach method designed for Failure could be useful. You can write your own with implicit conversion.
4

Something like this:

 def doTheDangerousThing[Result](dangerousOp: =>Result): Try[Result] = Try(dangerousOp) match { case o @ Failure(_) => println("error"); o case _ => _ } 

Comments

4

Not sure if this is more idiomatic, but sometimes I find that placing the recoverWith in this manner improves readability for me:

def doDangerousThing(): Try[Result] = Try { dangerousOp } recoverWith { case t: Throwable => println("error"); Failure(t) } 

Comments

4

My preferred,

def doTheDangerousThing(): Option[Result] = Try (dangerousOp) toOption 

If the Try is successful you will get a Some(value), if it fails a None.

For a large compilation on Try uses, have a look at Try introduced in Scala 2.10.0 .

Comments

2

Well, I suppose you could do something like this:

def doTheDangerousThing(): Option[Result] = Try(dangerousOp) match { case Success(result) => Some(result) case Failure(e) => None //might want to log the error as well } 

6 Comments

How is this code a replacement for the snippet in the question? What's the point of changing the return type? I think Pablo was just interested in replacing the if statement.
There is already toOption method in Try. And yes, it's not an answer.
This changes the return type and loses the Exception
This is how I would do this. Deal with the exception here, instead of passing the Try up. I hinted at this in the solution... add some code to log and/or deal with the exception in the case Failure(e) -- in which case it's superior to just using toOption.
I'll also add, I changed the return type because I'm dubious on the merit of examining the value within the Try in one place, and then returning the Try.. presumably so that it will be examined again somewhere else. Are you going to have exception-handling code in two places?
|
2

There are ways. For instance:

def doTheDangerousThing(): Try[Result] = { val result = Try(dangerousOp) result.failed foreach { _ => println("error") } result } 

Or, if you don't want to repeat result all through, then:

def doTheDangerousThing(): Try[Result] = { Try(dangerousOp) recover { case ex => println("error"); throw ex } } 

1 Comment

@santiagomaldonado So? I'm calling foreach on that. It should ignore any Failure results, and work on any Success results, which seems to be exactly what I wanted (back 4 years ago, so please understand I have no recollection of this :)).
1

In some cases I love to use two-step approach which will allow me a more granular error message control:

 def retrieveData(dataId: String): Try[String] = { Try { Option(someApi(dataId)) .getOrElse(throw SomeApiFailedException("invalid dataId")) } recoverWith { case e: SomeApiFailedException => Failure(e) case e: Throwable => Failure(SomeApiFailedException("failed retrieve dataId")) } } case class SomeApiFailedException(err: String) extends RuntimeException(err) 

Comments

0

I could choose from either of the three implementations, depending on whether I want to:

  • Simply propagate it upwards ( doTheDangerousThing1 )
  • Ignore the error ( doTheDangerousThing2 )
  • Intercept the error while propagating it upwards ( doTheDangerousThing3 )

Here is the code:

import scala.util.{Try,Success,Failure} object temp { type Result = Int def dangerousOp = { val r = scala.util.Random.nextInt(10) if (r > 5) r else throw new RuntimeException("Failed on " + r) } def logMessage[T](t: T) = println(t) def doTheDangerousThing1(): Try[Result] = Try(dangerousOp) def doTheDangerousThing2(): Option[Result] = { Try(dangerousOp) match { case Success(r) => Option(r) case _ => None } } def doTheDangerousThing3(): Try[Result] = { Try(dangerousOp) match { case t @ Success(r) => t case t @ _ => logMessage("failed: "+t); t } } } 

Inside the REPL

scala> doTheDangerousThing1 res0: scala.util.Try[Result] = Success(9) scala> doTheDangerousThing1 res1: scala.util.Try[Result] = Success(9) scala> doTheDangerousThing2 res2: Option[Result] = None scala> doTheDangerousThing2 res3: Option[Result] = Some(7) scala> doTheDangerousThing3 failed: Failure(java.lang.RuntimeException: Failed on 0) res4: scala.util.Try[Result] = Failure(java.lang.RuntimeException: Failed on 0) scala> doTheDangerousThing3 failed: Failure(java.lang.RuntimeException: Failed on 0) res5: scala.util.Try[Result] = Failure(java.lang.RuntimeException: Failed on 0) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.