views:

562

answers:

3

In the upcoming scala 2.8, a util.control package has been added which includes a break library and a construct for handling exceptions so that code which looks like:

type NFE = NumberFormatException
val arg = "1"
val maybeInt = try { Some(arg.toInt) } catch { case e: NFE => None }

Can be replaced with code like:

import util.control.Exception._
val maybeInt = catching(classOf[NFE]) opt arg.toInt

My question is why? What does this add to the language other than providing another (and radically different) way to express the same thing? Is there anything which can be expressed using the new control but not via try-catch? Is it a DSL supposed to make exception-handling in Scala look like some other language (and if so, which one)?

+3  A: 

I'd say it's foremost a matter of style of expression.

Beyond being shorter than its equivalent, the new catching() method offers a more functional way to express the same behavior. Try ... catch statements are generally considered imperative style and exceptions are considered side-effects. Catching() puts a blanket on this imperative code to hide it from view.

More importantly, now that we have a function then it can be more easily composed with other things; it can be passed to other higher-order functions to create more sophisticated behavior. (You cannot pass a try .. catch statement with a parametrized exception type directly).

Another way to look at this is if Scala didn't offer this catching() function. Then most likely people would 're-invent' it independently and that would cause code duplication, and lead to more non-standard code. So I think the Scala designers believe this function is common enough to warrant including it in the standard Scala library. (And I agree)

alex

Alex Boisvert
Can you point to a resource where *"exceptions are considered side-effects"*? How is an exception (which does not necessarily alter the state of the target) a side-effect
oxbow_lakes
Also, I agree. Clearly it is a matter of style... but having 2 so completely different ways of achieving the same outcome is highly dangerous in my opinion because it will lead to maintenance issues.
oxbow_lakes
Could you give an example of a method which takes a *catching* expression but could not be designed to take a `try-catch`?
oxbow_lakes
+12  A: 

There are two ways to think about exceptions. One way is to think of them as flow control: an exception changes the flow of execution of the program, making the execution jump from one place to another. A second way is to think of them as data: an exception is an information about the execution of the program, which can then be used as input to other parts of the program.

The try/catch paradigm used in C++ and Java is very much of the first kind(*).

If, however, if you prefer to deal with exceptions as data, then you'll have to resort to code such as the one shown. For the simple case, that's rather easy. However, when it comes to the functional style where composition is king, things start to get complicated. You either have to duplicate code all around, or you roll your own library to deal with it.

Therefore, in a language which purports to support both functional and OO style, one shouldn't be surprised to see library support for treating exceptions as data.

And note that there is oh-so-many other possibilities provided by Exception to handle things. You can, for instance, chain catch handlers, much in the way that Lift chain partial functions to make it easy to delegate responsibility over the handling of web page requests.

Here is one example of what can be done, since automatic resource management is in vogue these days:

def arm[T <: java.io.Closeable,R](resource: T)(body: T => R)(handlers: Catch[R]):R = (
  handlers 
  andFinally (ignoring(classOf[Any]) { resource.close() }) 
  apply body(resource)
)

Which gives you a safe closing of the resource (note the use of ignoring), and still applies any catching logic you may want to use.

(*) Curiously, Forth's exception control, catch&throw, is a mix of them. The flow jumps from throw to catch, but then that information is treated as data.

EDIT

Ok, ok, I yield. I'll give an example. ONE example, and that's it! I hope this isn't too contrived, but there's no way around it. This kind of thing would be most useful in large frameworks, not in small samples.

At any rate, let's first define something to do with the resource. I decided on printing lines and returning the number of lines printed, and here is the code:

def linePrinter(lnr: java.io.LineNumberReader) = arm(lnr) { lnr =>
  var lineNumber = 0
  var lineText = lnr.readLine()
  while (null != lineText) {
    lineNumber += 1
    println("%4d: %s" format (lineNumber, lineText))
    lineText = lnr.readLine()
  }
  lineNumber
} _

Here is the type of this function:

linePrinter: (lnr: java.io.LineNumberReader)(util.control.Exception.Catch[Int]) => Int

So, arm received a generic Closeable, but I need a LineNumberReader, so when I call this function I need to pass that. What I return, however, is a function Catch[Int] => Int, which means I need to pass two parameters to linePrinter to get it to work. Let's come up with a Reader, now:

val functionText = """def linePrinter(lnr: java.io.LineNumberReader) = arm(lnr) { lnr =>
  var lineNumber = 1
  var lineText = lnr.readLine()
  while (null != lineText) {
    println("%4d: %s" format (lineNumber, lineText))
    lineNumber += 1
    lineText = lnr.readLine()
  }
  lineNumber
} _"""

val reader = new java.io.LineNumberReader(new java.io.StringReader(functionText))

So, now, let's use it. First, a simple example:

scala> linePrinter(new java.io.LineNumberReader(reader))(noCatch)
   1: def linePrinter(lnr: java.io.LineNumberReader) = arm(lnr) { lnr =>
   2:          var lineNumber = 1
   3:          var lineText = lnr.readLine()
   4:          while (null != lineText) {
   5:            println("%4d: %s" format (lineNumber, lineText))
   6:            lineNumber += 1
   7:            lineText = lnr.readLine()
   8:          }
   9:          lineNumber
  10:        } _
res6: Int = 10

And if I try it again, I get this:

scala> linePrinter(new java.io.LineNumberReader(reader))(noCatch)
java.io.IOException: Stream closed

Now suppose I want to return 0 if any exception happens. I can do it like this:

linePrinter(new java.io.LineNumberReader(reader))(allCatch withApply (_ => 0))

What's interesting here is that I completely decoupled the exception handling (the catch part of try/catch) from the closing of the resource, which is done through finally. Also, the error handling is a value I can pass on to the function. At the very least, it makes mocking of try/catch/finally statements much easier. :-)

Also, I can combine multiple Catch using the or method, so that different layers of my code might choose to add different handlers for different exceptions. Which really is my main point, but I couldn't find an exception-rich interface (in the brief time I looked :).

I'll finish with a remark about the definition of arm I gave. It is not a good one. Particularly, I can't use Catch methods such as toEither or toOption to change the result from R to something else, which seriously decreases the value of using Catch in it. I'm not sure how to go about changing that, though.

Daniel
*Daniel* - a thoughtful answer as always but could you extend the answer to provide 2 different examples of calling your `arm` method? I can see that you can supply different handlers but then you could do that in the imperative style by catching the exceptions at the call site...
oxbow_lakes
All turing-complete languages are equivalent. You can do "filter" without high-order functions, it's just more awkward. I'll try to think of a good example to illustrate how this is different.
Daniel
As a side note, I avoided writing an example in first place because the Java APIs that implement Closeable are horrible. I didn't want to go through the pain! :-)
Daniel
I do worry that there are too many toys in scala 2.8 which, whilst a powerful aide to library-designers, will be disastrous if eagerly jumped-upon by most developers. Josh Bloch gave an excellent talk in 2008 about the power-to-weight ratio of language changes in Java. I think this could also apply to some of these features in Scala.
oxbow_lakes
Java suffers from not having traits, only interfaces.
Daniel
Top answer, Daniel. I wonder whether there's a badge for consistently long answers...
oxbow_lakes
+4  A: 

As the guy who wrote it, the reasons are composition and encapsulation. The scala compiler (and I am betting most decent sized source bases) is littered with places which swallow all exceptions -- you can see these with -Ywarn-catches -- because the programmer is too lazy to enumerate the relevant ones, which is understandable because it's annoying. By making it possible to define, re-use, and compose catch and finally blocks independently of the try logic, I hoped to lower the barrier to writing sensible blocks.

And, it's not exactly finished, and I'm working on a million other areas too. If you look through the actors code you can see examples of huge cut-and-pasted multiply-nested try/catch/finally blocks. I was/am unwilling to settle for try { catch { try { catch { try { catch ...

My eventual plan is to have catch take an actual PartialFunction rather than require a literal list of case statements, which presents a whole new level of opportunity, i.e. try foo() catch getPF()

extempore
Oh, and I forgot to mention my eventual plan is to have catch take an actual partialfunction rather than require a literal list of case statements, which presents a whole new level of opportunity, i.e. try foo() catch getPF()
extempore