This page is no longer maintained — Please continue to the home page at www.scala-lang.org

exception handling composition

No replies
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.

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))
}

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland