0

I need to use some Java library, which might throw some exceptions in one method and return error codes in another set of methods. So far it leads to the ugly code like

val txn = mgr.prepareTransaction()
val accessRecord = txn.readByQuery(...)
var state : Either[MyError, Result] = null //
try {
  // do something here
  val result = txn.runCodeWithin(new Callable[Result]() {...})
  if (result == -1) {
    state = Left(CanNotReadRecord)
  } else {
    state = Right(txn.getCachedRecord())
  }
} catch {
  case e: Exception => state = Left(GeneralError(e))
} finally {
  state match {
    case Right(_) => txn.commit();
    case _        => txn.rollback();
  }
}

I mostly interested in getting rid of state as var and ability to check the state in finally block. Please advice.

jdevelop
  • 12,176
  • 10
  • 56
  • 112
  • If you have the same pattern all over, you can factor the ugliness into a single utility method that takes 3 arguments (the mgr, the arg to readByQuery and a block to create the callable for runCodeWithin). Then use the utility method instead. It won't make this particular method pretty but that at least will reduce its footprint. – huynhjl Feb 10 '13 at 02:47
  • that's what I'm actually doing - extract transaction management and cursor into utility method with implicit error producer. However it still looks ... weird. – jdevelop Feb 10 '13 at 03:29

2 Answers2

4

Scala 2.10 introduced the Try class, which is a more functional replacement to the use case of Either[Throwable, Result]. It's got all of the usual monad ops (the things that make for-comprehensions work), and some other helpful methods. (check out the docs for Try here)

Here's a possible re-implementation of your code, using Try, and replacing CanNotReadRecord with a CanNotReadRecordException. It should be functionally equivalent to your example, with the exception of that replacement.

def txResults(txn: Transaction): Try[Record] = for {
    result <- Try{ txn.runCodeWithin(...) }
    checked <- result match {
        case -1 => Failure( new CanNotReadRecordException )
        case _ => Success( txn.getCachedRecord )
    }
} yield checked

txResults(txn) match {
    case Success(record) => txn.commit()
    case Failure(e) => txn.rollback() //and maybe handle `e`
}
Dylan
  • 13,645
  • 3
  • 40
  • 67
3

The Scala ARM (Automatic Resource Management) library handles all this sort of thing elegantly and in a completely air-tight manner.

Check it out.

Randall Schulz
  • 26,420
  • 4
  • 61
  • 81
  • 1
    Upvoted though I think using the library would still be non trivial in this particular example, because depending on `result` you have an error or a record, so that would yield a `Either[MyError, Result]`. So using `resource.either` would yield a `Either[Seq[Exception], Either[MyError, Result]]` that would have to be combined into `Either[MyError, Result]`. – huynhjl Feb 10 '13 at 02:42