scala 2.8 control Exception - what is the point?

后端 未结 3 1965
陌清茗
陌清茗 2021-01-31 19:27

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 lik

相关标签:
3条回答
  • 2021-01-31 19:34

    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

    0 讨论(0)
  • 2021-01-31 19:36

    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.

    0 讨论(0)
  • 2021-01-31 19:37

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

    0 讨论(0)
提交回复
热议问题