I have several blocks of code that follow this pattern:
// Dummy function defs.
def result(i : Int, d : Double, b : Boolean) = {
if (b) d else i
}
def fA(s : String) = {7}
def fB(s : String, i : Int) = {1.0}
def fC(s : String, i : Int, d : Double) = {true}
// Actual code.
def test(s : String) : Double = {
try {
val a = fA(s)
try {
val b = fB(s, a)
try {
val c = fC(s, a, b)
result(a, b, c)
} catch {
case _ => result(a, b, false)
}
} catch {
case _ => result(a, 0.0, false)
}
} catch {
case _ => result(0, 0.0, false)
}
}
Where a, b, & c are calculated in turn by the corresponding functions and then the values are passed to the result function. If at any stage an exception occurs then a default value is used in place of the remaining variables.
Is there a more idiomatic way to express this code. It reminds me of Monads in that it's a series of chained computations which bail out immediately if any computation fails.
These types of problems are just what Try
aims to solve a bit more monadically (than nested try/catch
blocks).
Try
represents a computation that may either result in an exception, or return a successfully computed value. It has two subclasses for these-- Success
and Failure
.
Very funny that this question popped up when it did-- a few days ago, I finished up some additions and refactoring to scala.util.Try
, for the 2.10 release and this SO question helps to illustrate an important use-case for a combinator that we eventually decided to include; transform
.
(As of writing this, transform
is currently in the nightly and will be in Scala from 2.10-M5 onward, due out today or tomorrow. More info about Try
and usage examples can be found in the nightly docs)
With transform
(by nesting them), this can be implemented using Try
s as follows:
def test(s: String): Double = {
Try(fA(s)).transform(
ea => Success(result(0, 0.0, false)), a => Try(fB(s, a)).transform(
eb => Success(result(a, 0.0, false)), b => Try(fC(s, a, b)).transform(
ec => Success(result(a, b, false)), c => Try(result(a, b, c))
)
)
).get
}