- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
exception handling composition
Wed, 2009-04-29, 21:29
I played with this today and was pretty happy with what can be
accomplished thanks to partial function composition and by-name
arguments. Independently of the catching too much issue, I have always
found try/catch/finally syntax to be a wart. To be able to encapsulate
a particular catch/finally logic and then apply that to different bodies
is really nice.
Here is some sample code:
val plausiblyImportantExceptions: PartialFunction[Throwable,Nothing] = {
// not intended to be exhaustive list
case x @ (_: ThreadDeath | _: InterruptedException | _: VirtualMachineError) => throw x
}
scala> new CatchLogic(o.plausiblyImportantExceptions
orElse { case x => "I swallow everything" })
res1: scala.util.control.CatchLogic[java.lang.String] = scala.util.control.CatchLogic@b99561
scala> res1 invokeOn { throw new ThreadDeath }
java.lang.ThreadDeath
at $anonfun$1.apply(:12)
at $anonfun$1.apply(:12)
[snip]
scala> res1 invokeOn { null.toString }
res4: java.lang.String = I swallow everything
And here's what the code look like at this second. Interested in
whether people see a future here, and any other ideas for making it nicer.
package scala.util.control
/** Experimental methods for exception handling composition.
* @author Paul Phillips
*/
class CatchLogic[+T](pf: PartialFunction[Throwable,T], fin: => Unit) {
def this(pf: PartialFunction[Throwable,T]) = this(pf, ())
def orElse[U >: T](pf2: PartialFunction[Throwable,U]) = new CatchLogic(pf orElse pf2, fin)
def orElse[U >: T](clazz: Class[_ <: Throwable], res: => U) = {
val pf2 = new PartialFunction[Throwable,U] {
def apply(x: Throwable) = res
def isDefinedAt(x: Throwable) = clazz.isAssignableFrom(x.getClass)
}
new CatchLogic(pf orElse pf2, fin)
}
def alsoFinally(fin2: => Unit) = new CatchLogic(pf, { fin ; fin2 })
def invokeOn[U >: T](body: => U): U =
try { body }
catch { case e if pf.isDefinedAt(e) => pf(e) }
finally { fin }
}
class TryLogic[+T](body: => T, val catcher: CatchLogic[T]) {
def invoke() = catcher invokeOn body
def orElse[U >: T](pf2: PartialFunction[Throwable,U]) =
new TryLogic[U](body, catcher orElse pf2)
def alsoFinally(fin2: => Unit) =
new TryLogic(body, catcher alsoFinally fin2)
}
object Throwable {
def emptyCatches[T]() = new PartialFunction[Throwable,T] {
def isDefinedAt(x: Throwable) = false
def apply(x: Throwable) = null.asInstanceOf[T]
}
def tryCatch[T](body: => T)(pf: PartialFunction[Throwable,T]) =
new TryLogic(body, new CatchLogic(pf))
def tryFin[T](body: => T)(fin: => Unit) =
new TryLogic(body, new CatchLogic(emptyCatches[T](), fin))
def tryCatchFin[T](body: => T)(pf: PartialFunction[Throwable,T])(fin: => Unit) =
new TryLogic(body, new CatchLogic(pf, fin))
}