3
\$\begingroup\$
override def get(key: String): Option[Any]  = {
  val timer = couchbaseMetricsTimer.time()
  try { // Any way (is it more elegant?) to avoid the try/catch/finally?
    Option(couchBaseClient.get(key))
  } catch {
    case e: Exception => couchbaseFailureMetricCounter.inc(); throw e
  } finally {
    timer.stop()
  }
}

Note the clients of this code are expecting exceptions in case of failures.

\$\endgroup\$
  • \$\begingroup\$ If you are just going to re-throw the exception, why bother catching it at all? Just try-finally should suffice. \$\endgroup\$ – user50399 Aug 26 '14 at 15:24
  • \$\begingroup\$ because i'm calling a failure metric couchbaseFailureMetricCounter.inc() to increment failure counter whenever i get an exception. \$\endgroup\$ – Jas Aug 27 '14 at 3:50
  • 1
    \$\begingroup\$ scala-lang.org/api/current/… You may want take a look at this \$\endgroup\$ – jilen Sep 2 '14 at 8:12
3
\$\begingroup\$

You could use Try utility.

But the main concern is that it doesn't support finally statement out-of-the-box.

This can be achieved with some Try enhancement's value class (by Viktor Klang):

implicit class TryOps[T](val t: Try[T]) extends AnyVal {
  def eventually[Ignore](effect: => Ignore): Try[T] = {
    val ignoring = (_: Any) => { effect; t }
    t transform (ignoring, ignoring)
  }
}

Consider this code:

val timer = couchbaseMetricsTimer.time()
val tryClient =
  Try(couchBaseClient.get(key)) map { //convert success result
    Option(_)
  } recover { //process failure result
    case e: Exception => couchbaseFailureMetricCounter.inc(); throw e
  } eventually { //perform "finally" block
    timer.stop()
  }

//return actual result
tryClient.get

It's not less, by more idiomatic, so it's up to you which way to choose.

Here are more references to discover.

Note: you can put TryOps into package object of some of your root package f.e. com.mycompany, and then you can use it everywhere in your project.

\$\endgroup\$
  • 2
    \$\begingroup\$ It's nice that Scala allows to create objects capable of doing what language constructs usually do, but is there any advantage of doing it in this case? The original code is perfectly clear to me (as a Java programmer), the rewritten is sort of clear too, but only because it looks about the same. \$\endgroup\$ – maaartinus Aug 29 '14 at 18:30
  • 2
    \$\begingroup\$ In this particular case there seem no difference. But in general, Try is monadic construction, which allows you to work with it correspondingly, e.g. avoid unnecessary checks and use chaining calls (f.e. several maps in a row). Also Try can be perfectly useful, when you don't need to have exception thrown immediately in the same thread. By the link I provided in answer more information about features of Try. \$\endgroup\$ – n1ckolas Aug 29 '14 at 18:45
1
\$\begingroup\$

An interesting approach to this might be to treat the timer as a resource using the scala-arm library.

You could provide your own implicit implementation of the Resource type class for the timer so that it would automatically be stopped when it exits the managed scope or if an exception occurs. Ideally you could also increment your analytics counter in the resource implementation. This would completely eliminate the need for the try/catch/finally. This would only work, however, if the couchbaseFailureMetricCounter can somehow be referenced there. I would have to see the code or scaladocs for the metrics classes to understand whether that would make sense or not.

The concept would go something like this:

import resource._

override def get(key: String): Option[Any]  =
  managed(couchbaseMetricsTimer.time()) acquireAndGet { _ ⇒
    Option(couchBaseClient.get(key))
  }

And then, somewhere in the implicit scope, provide the Resource implementation for the timer type, here assumed to be Timer. The preferred place to put it would be in the companion object of the Timer class (if you own the code for that class):

import scala.resource.Resource

object Timer {
  implicit object TimerResource extends Resource[Timer] {
    def close(timer) = timer.stop()
    def closeAfterException(timer, throwable) = {
      // not sure where this counter would come from, perhaps
      // it could be mixed in or imported
      couchbaseFailureMetricCounter.inc()
      // super implementation also calls close
      super.closeAfterException(timer, throwable)
    }
  }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.