Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala DRYing try/catch

I find myself repeating simple try/catch blocks that should, IMO, be 1-liners.

For example, let's say I need to convert a uri string date in yyyymmdd format to Joda-Time. With a standard try/catch block the conversion method looks like:

def ymd2Date(ymd: Option[String]) = ymd match {
  case Some(date) =>
    try { Right( ymdFormat.parseDateTime(date) ) }
    catch { case e => 
      log.info(e.getMessage)
      Left(None) 
    }
  case None => 
    Left(None) // no uri date param
}
val ymdFormat = DateTimeFormat.forPattern("yyyyMMdd")

Works well enough, intent is clear, but when I do this kind of try/catch logging for all non-critical events, then I seek a way to DRY it out. The search led me to this SO post on scala.util.control.Exception. Helpful, but it's still a bit difficult to grok/make it work in the way I'd like it to. In plain English I just want to say, "catch some-action get-result log-error-type".

So, I hacked this out based on the part of control.Exception I'm interested in (or understand to be useful):

class Catcher[T](f: => T) {   
  type Logger = (=> Any) => Unit

  def either[T]( logger: => Logger ) = {
    try { Right(f) }
    catch { case e => 
      logger(e.getMessage)
      Left(None)
    }
  }
}
def catching[T](f: => T) = new Catcher(f)

And then use in place of try/catch like so:

catching( ymdFormat.parseDateTime(date) ) either log.info

Can add on option, a msg prefix, etc., but...it would probably be better to find a way to get control.Exception to work like the above, as the TypeSafe crew is going to produce code worlds better than I'll ever imagine writing.

Does anyone know how to create this kind of syntax using control.Exception where one can pass in a logger function by-name to be used in the catch block?

Would be great if there was a "use cases" for control.Exception, but I get the feeling this utility is for more advanced Scala devs

like image 619
virtualeyes Avatar asked Apr 11 '12 16:04

virtualeyes


2 Answers

This should do what you want:

import scala.util.control.Exception
def log(logger: => Logger)(e: Throwable) = {
    logger(e.getMessage)
    None
}
Exception.allCatch withApply log(logger) apply Some(ymdFormat.parseDateTime(date))

But this kind of stuff is better handled by Scalaz Validation, in my opinion.

like image 164
Daniel C. Sobral Avatar answered Nov 16 '22 01:11

Daniel C. Sobral


A quick example:

import scala.util.control.Exception._

def throwingStuff {
  throw new Exception("Hello World!")
}

catching(classOf[Exception]).withApply{err => println(err.toString); None}.apply(Some(throwingStuff))

You can use withApply to override the application logic of the Catch class to do something such as writing to a log.

like image 37
Submonoid Avatar answered Nov 15 '22 23:11

Submonoid