0

Suppose I have a function f: Int => String which may throw exceptions. I would like to write a function tryF(f: Int => String, arg: Int, defaultOpt: Option[String]), which works as follows:

  • Invoke f(arg) and return the result if it has thrown no exception
  • if the invocation has failed with an exception and defaultOpt.isDefined then return defaultOpt.get
  • Otherwise throw the exception

I am writing it as follows:

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {
  val r = scala.util.Try(f(arg))
  r.toOption orElse defaultOpt getOrElse r.get
}

Does it make sense ? Would you rewrite it ?

5 Answers 5

5

Your solution is fine even though I personally don't find it very readable. I would do something like that:

def tryF[A](f: =>A, defaultOpt: Option[A]): A = (Try(f), defaultOpt) match {
  case (Success(result), _)          => result
  case (Failure(_), Some(defValue))  => defValue
  case (Failure(t), None)            => throw t
}

Note that it is unnecessarily verbose (I could have used wildcard in some of the cases) but I think your use case deserves some self documenting code. It is easy to understand what triggers each case.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. The pattern matching was the alternative. It looks verbose but probably more readable (for most people at least).
2

A verbose, but easy-to-understand way...

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

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {
 Try(f(arg)) match {
      case Success(r) => r
      case Failure(ex) => defaultOpt match {
           case Some(content) => content
           case None => throw ex
      } 
 }

Slightly more compact way:

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

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = 
 Try(f(arg)) match {
      case Success(r) => r
      case Failure(ex) => defaultOpt getOrElse {throw ex}
 }

4 Comments

I would use defaultOpt getOrElse {throw ex} instead of the nested pattern matching.
agreed, but outer pattern matching is still needed to get ex
I like the 2nd solution. Thank you.
I don't think using Try is appropriate in this instance and you would be better using try/catch as answered by Dan Getz. I would use the Try as the return type indicating success or failure.
2

No need for Try in your case; try/catch is enough:

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {
  try {
    f(arg)
  } catch {
    case e: Exception => defaultOpt getOrElse { throw e }
  }
}

If you want to use Try, I'd suggest taking advantage of recover:

def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {
  Try {
    f(arg)
  }.recover({
    case _ if defaultOpt.isDefined => defaultOpt.get
  }).get
}

However, if this is your actual function, I'd consider rewriting the argument types to be generic, rather than rewriting the implementation, as long as the implementation already works:

def tryF[A, B](f: A => B, arg: A, defaultOpt: Option[B]): B = /* ... */

2 Comments

(This is not snark, at least, not intentionally) Try { f(arg) }.recover(Function.unlift(Function.const(defaultOpt))).get - is that really readable (as in "you can clearly see what it is doing - i.e. conditionally throwing the exception?
You're right, that was pretty unreadable. Got rid of the Function._ stuff.
1
def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {
  val r = scala.util.Try(f(arg))
  (r, defaultOpt) match {
    case (Success(result), _) => result
    case (Failure(_), Some(opt)) => opt
    case (Failure(ex), None) => throw ex
}

Comments

1
def tryF(f: Int => String, arg: Int, defaultOpt: Option[String]) = {

    val r = scala.util.Try(f(arg))

    if (r.isSuccess) r.get else defaultOpt.getOrElse(r.get)

    // reads like -> if r is success, get from r, else get from default, else get from r
}

1 Comment

If defaultOpt is None I need to throw the original exception.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.