Reputation: 42050
This is a follow-up for my previous question.
Suppose I have functions foo
, bar
, and qux
that raise exceptions.
def foo(x: Int, y: Int, z: Int): String = ...
def bar(s: String): String = ...
def qux(t: String): String = ...
I am composing them as follows:
def myFun(x: Int, y: Int, z: Int): String = {
val s = foo(x, y, z)
val t = bar(s)
qux(t)
}
What if bar
raises an exception ? If the exception does not contain s
I cannot understand what happened. I probably need to add s
to that exception. (The same applies to Either
, \/
, etc.)
So I am defining an exception, which "captures" the arguments:
case class MyException(e: Exception, args: Any*) extends Exception(e)
and writing myFun
as follows:
def myFun(x: Int, y: Int, z: Int): String = {
def myFoo(x: Int, y: Int, z: Int) = try foo(x, y, z) catch {
case NonFatal(e: Exception) => throw MyException(e, x, y, z)
}
def myBar(s: String) = try bar(s) catch {
case NonFatal(e: Exception) => throw MyException(e, s)
}
def myQux(t: String) = try qux(t) catch {
case NonFatal(e: Exception) => throw MyException(e, t)
}
val s = myFoo(x, y, z)
val t = myBar(s)
myQux(t)
}
I have added a lot of code now but I can catch the exception and print it out to see the data I need.
Does it make sense ? Is it possible to get rid of the boilerplate code in myFoo
, myBar
, myQux
? Does Shapeless help here?
Upvotes: 0
Views: 278
Reputation: 37435
I think you are trying to patch a deeper issue here. In principle, foo
, bar
, and qux
should raise meaningful exceptions. Joshua Bloch in his book "Effective Java" extends on this point where meaningful Exceptions should be constructed with the parameters that help track back the issue at hand.
Imagine this principle applied to your question:
def foo(a:Int, b:Int, c:Int):String = if (a<0) throw new ParameterOutOfBounds("a","lt 0") else s"a->$a,b->$b,c->$c"
def bar(s:String):String = if (s.endsWith("0")) throw IllegalArgumentException(s"Unacceptable suffix ${s.last}") else s
def qux(s:String):String = if (s.endsWith("1")) throw IllegalArgumentException(s"Unacceptable suffix ${s.last}") else s
You can compose these functions and still preserve any Exception on the composition:
def composed(a:Int, b:Int, c:Int):Try[String] =
for { vfoo <- Try(foo(a,b,c));
vbar <- Try(bar(vfoo));
vqux <- Try(qux(vfoo))}
yield (vqux)
scala> composed(1,2,3)
res4: scala.util.Try[String] = Success(a->1,b->2,c->3)
scala> composed(-1,2,3)
res7: scala.util.Try[String] = Failure(ParameterOutOfBounds: Invalid Parameter=a. Reason=lt 0)
scala> composed(1,2,0)
res5: scala.util.Try[String] = Failure(java.lang.IllegalArgumentException: Unacceptable suffix 0)
In case foo,bar and qux
are code you cannot modify, I'd wrap those exceptions using the same principle of "something meaningful" instead of packaging their parameters in a generic exception.
Upvotes: 2