Michael
Michael

Reputation: 42050

How to re-write this code idiomatically in Scala?

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:

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 ?

Upvotes: 0

Views: 199

Answers (5)

Dan Getz
Dan Getz

Reputation: 9134

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 = /* ... */

Upvotes: 2

Ashalynd
Ashalynd

Reputation: 12563

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}
 }

Upvotes: 2

Shyamendra Solanki
Shyamendra Solanki

Reputation: 8851

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
}

Upvotes: 1

wedens
wedens

Reputation: 1830

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
}

Upvotes: 1

vptheron
vptheron

Reputation: 7466

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.

Upvotes: 5

Related Questions