My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
ProposalForErrors  
Distinguish expected errors from exceptions.
Proposal
Updated Sep 20, 2009 by aeagle22206

Introduction

Checked exceptions cause a lot of problems, and don't buy much, so Noop only has unchecked exceptions.

Coders often use exceptions for expected error states. An example:

// in Java
FileOutputStream out;
try {
  out = new FileOutputStream(new File("/tmp/foo"));
} catch (FileNotFoundException e) {
  // handle it
}
out.readline();

Using exceptions to check for "expected" or "likely" error conditions is usually not an appropriate use of exceptions. Creating the exception requires capturing a stack trace, and the try-catch block interrupts the control flow and creates the ugly scope, in order to provide a reference to the exception. In this example, it requires declaration of the variable out in a null state so it must be mutable.

This Java code could check for the file existence before trying to read from the file.

File file = new File("/tmp/foo");
if (file.exists()) {
  FileOutputStream out = new FileOutputStream(file);
  out.readline();
} else {
  // handle it
}

But, this requires the File API to do some work twice. Some API's are especially bad for this sort of work, like having to run two database calls instead of one just to support this conditional.

This proposal allows a way to hand an error back from a single API call in a more declarative way than with an unchecked exception, without the scoping issues and stack trace creation in an exception.

Details

In the spirit of C's error out variables, some methods may declare that they return an optional error in addition to the returned value. This allows the caller to handle the error in a normal control flow.

Here is an example with another common case, parsing a number from a String. A failure to parse is an expected error.

class Int() {
  Int, Error parse(String s) {...}
}

Int a, Error e = new Int().parse("13a");
if (e.isError()) {
  // handle error
} else {
  // a must be a good int
}

We can return some sentinel value for a in this case, like zero. This has some advantages if the caller doesn't care to check this for an error, ie. there is some other validation of the value.

The caller should be able to ignore the error, also.

Int a, _ = new Int().parse("100");
Comment by jfpoilp...@gmail.com, Aug 23, 2009

I think with this proposal you are going back to the terrible API of C standard libraries, even though you may not want it, others will do it for you!

What you describe in your Java example IS an exception and should be handled as such, because you should not call new FileOutputStream?() before checking the File exists.

The only problem with the Java API in this case is that IOException is checked so you HAVE to deal with it even though you don't want to (because you consider you dealt with it when first checking that the file existed)!

Comment by project member aeagle22206, Aug 24, 2009

Yeah I'm not really fond of this one even though I wrote it up. It bothers me that there's no real distinction between Errors and Exceptions.

I think we agree that exceptions will be unchecked, so that may encourage people to do the right thing and check that the file exists first.

Comment by project member gabri...@gmail.com, Sep 7, 2009

Maybe we leave it up to the libraries to have exception or error "based" methods.. For example,

Int a = new Int().parse("a"); // Throws an exception

Int a = new Int().parseOr("a", 0); // a => 0

Or we have a way to catch and return a value in case of an exception at runtime, though feels bad.

Int a = new Int().parse("a") or 0;

// Or even
Int a = a or 0;

(like ||= in Ruby).

Comment by rodion.m...@gmail.com, Sep 19, 2009

What about the case when the file doesn't exist at access time? After all, checking for file.exists() does not guarantee that it will exist, or be accessible when you ask the file-system for the handle. So maybe it's an "exceptional" case after all?

I think the main annoyance is the chunky boiler-plate that comes with try/catch/finally blocks, and it's worth looking at alternative ways of handling exceptional cases. I like the elegant propositions by gabrielh, where you specify the default values for when an exception has happened. Why not try something similar for file API?

FileInputStream in = new FileInputStream("/maybeExisting.file", OnFileNotFound.EmptyDataInputStream);
// No language intervention, just library support.

The library could provide a set of possible resolutions for the anticipated exceptional cases, and clean way of handling the rest of unhandled cases.

FileOutputStream out = new FileOutputStream("/maybeExisting.file", 
  OnFileNotFound.MakeDirsAndCreateFile, // Directory did not exist? try making it ...
  new FileNotFoundLastResortHandling()); // When everything fails call this

// Some modularity potential ...
// FileNotFoundLastResortHandling.java
class FileNotFoundLastResortHandling implements ExceptionHandler{
  @Override public void handle(FileNotFoundException e) {
    //log it ...
    //rethrow
    throw new FatalRuntimeException(e);
  }
}

Anyway, having a checked FileNotFoundException in my opinion, is a good way of making sure the developer does the right thing and handles the case. I doubt unchecked exception will encourage anyone to check for file existence, probably right the opposite.

The finally{ close(); } idiom could be cleaned-up with a "with" statement such as the one proposed in Python 2.5(The with statement). I think there is a similar effort in JDK7.

Comment by Arnold.B...@gmail.com, Sep 22, 2009

What if we ONLY had unchecked exceptions.

A consequence would be that in coding methodA() (which calls methodB(), which calls methodC(), and so on, to methodQ() which throws a specific exception), how would I know in methodA() the types of exceptions that I would be expecting to be thrown?

It seems to me that a method signature needs to be able to specify the types of exceptions that are expected to be thrown, even as only pure documentation. You can think of it as part of the contract that is being entered into between the caller and called software.

Could we consider exceptions to be enforceable documentation which we can optionally choose to catch?

Comment by KnX.c...@gmail.com, Sep 23, 2009

You could have bearable checked exception if you used "abstract functions" (maybe not the best name).

Like a class which can be defined as abstract (allowed to be defined, but not used unless completed lated), an abstract function can be defined, but not yet used as is. She may only be called in the scope of another abstract function or a scope catching all checked exceptions the function may throw (either itself of because of other abstract function within its code).

Abstract property may be either explicitly written or infered (you union the exception-set of all function called in each try/catch scopes, remove the caught types, and union again till reaching the whole function scope, if non-empty the function is abstract and the computed set is its exception-set).

Comment by bear.am...@gmail.com, Sep 24, 2009

"Checked exceptions cause a lot of problems, and don't buy much, so Noop only has unchecked exceptions. " that will be a terrible error! the essence of a checked exception is that the caller does not have means to "know" about the conditions that will provoke an error. If he must know the state of a precondition then this is a runtimeException that the code will throw! once you know this don't tell me checked exception are a problem!!! now you may add features about preconditions/contracts ... but throwing out checked exception will be a very grave design error

Comment by marco.ro...@gmail.com, Sep 26, 2009

I've been reading a lot about checked vs. unchecked exceptions lately. This isn't my field, but I'd just like to offer some questions/comments.

Quick question. With the above proposals (original and gabrielh), how would unhandled runtime exceptions propogate? Since they aren't explictly thrown, they're just set as scope variables. Some people argue for specific exception types. But in my experience, most calling code trys to discern 1 thing. Is it a problem I can recover from or not? To try and clarify a little:

  • The caller can't recover from a file not found error. You could suggest that the caller may be able to create the file, but that's probably giving the caller too much responsibility. Essentially another code path should be started that may eventually culminate in calling the function again. But the original path is essentially dead in terms of exception handling.
  • A caller can recover from a Timeout exception in a network call. The method is idempotent in most cases, so it can just call again (and hope it was just a hiccup). Or it can pass a higher timeout. I think these are reasonable actions that are still within the caller's responsibility. If these don't work, it's time to execute another code path or propogate the exception.

So in my line of thinking, checked exceptions force a caller to consider whether it can handle any problems up front. You're more likely to include exception handling code because it's part of fulling the method signature. Only we've seen in practice that this is not the case. In many cases, the try/catch structure encourages programmers to treat all exceptions as "unfixable". Basically instead of handling the exception, most try/catches do one of the following.

  • Clean up before re-throwing
  • Wrap the exception in your own to throw down the stack
  • Ignore It

So if you buy all of this (and I'm not sure i do, this is just a line of thought) it looks like checked exceptions aren't that helpful. But having a mechanism for explicitly specifying the exceptions that are possible is very helpful.

Comment by Arnold.B...@gmail.com, Sep 28, 2009

Marco.rogers has given me some food for thought. Most 'catch' blocks in Java are not terribly interesting. Typically, there is only a small range of things that you could gainfully do. And we all know that junior developers can do it very poorly, often writing empty 'catch' blocks, where thrown exceptions are just caught silently.

Now, to specify the contract that a method implements, we need to say something about 1) the method name, 2) the method parameters, 3) the method result(s) and 4) the types of exceptions that are expected to be thrown. If we were to make a complete implementation, we might also specify pre-conditions and post-conditions of the method itself.

So the method signature can say something important about the exceptions that are expected to be thrown. So, perhaps we should consider reworking the 'try/catch' block to make life simpler. Perhaps it is not checked/unchecked exceptions that is the language problem but the way our 'try/catch' blocks work.

What about something like:

try
{
  .. some work in here ...
}
ignore (FileNotFoundException ex)
rethrow (IOException ex)
{
  if (printStream != null)
  {
    printStream.close();
  }
}
wrap (Exception ex as new ApplicationException(ex.getMessage(), ex));

Perhaps if we specialized the types of 'catch' blocks we permit, our junior developers wouldn't be able to write dubious exception handling code in the first place.

What do you think?

Comment by Arnold.B...@gmail.com, Sep 28, 2009

It seems to me that the argument over checked/unchecked exceptions goes something like this:

For use of checked exceptions:

  • The method signature should say something about the exceptions that are thrown, if for no other reason than as documentation of the contract that the caller is entering into.
  • Callers of a specific method appreciate that certain exceptions could be thrown and that they may need to include appropriate exception handling themselves. It prompts us to provide the appropriate robustness to handle these exceptions, when we can.
  • At any point in the code, we need to understand the types of exceptions that we could possibly catch, and this requires the methods that we call to explicitly specify their exceptions. How can we be assured that our overall error handling is correct, if we cannot be sure of the types of exceptions that are available to be caught?

For the use of ONLY unchecked exceptions:

  • The code is simpler because, in general, we don't need to specify the exceptions that could be thrown.
  • A change to one method to add an additional 'checked' exception to the method signature can have a snowball effect across many methods that ultimately call on the originally changed method. The 'thrown exception' may need to be propagated across many callers.
  • A number of exceptions are serious enough and rare enough that you don't want to have to specify them on all methods (eg. NullPointerException? or java.lang.InternalError?). Since any method could experience these, there is no point in including them in method signatures.

It seems the argument comes down to one of 'correctness' versus 'convenience'.

Comment by enrique....@gmail.com, Sep 30, 2009
I think the type of the exception is just one of the many possible metadatas that can be associated to a new exception. Checked exceptions naïvely try to handle and correct the exception by using its type as a hint to know "what happened". The exception type can or cannot be interesting for the code that tries to manage the raised exception.
Other useful metadata that could be associated to the exception could be the the timestamp, an automatic "serial number" and the context (thread, transaction, action/task beeing processed). In my own experience I have also found that most of the time when raising a new exception, tagging it based on who has to take care of it (an application user, a system administrator, a bugzilla system,... ), and not on what originated it (timeout, connection error, resource exhaustion, security alarm,...) is by far a more practical approach for production code.
A framework that allows to keep logs of metadata about exceptions so that a central exception controller can process them could be the way to go. Such metadata logs could contain the source of the exception (IOException, TimeoutException?, SecurityException?,...) and any other possible/useful metadata that can be handy.
This will allow to work with unchecked exceptions while keeping the info of checked exceptions if needed as well as many other useful info: For example if a central task's controller captures an uncheked exception, it could query what task was beeing processed, cancel it and remove any resource associated with the task at hand so the system comes back to an stable state. This controller doesn't care about what originated the exception, it just try to come back to an stable state. A connection controller can capture the unchecked exception and this time, based on the source of the error (timeout, no route to host, dns error,...) retry or cancel the connection raising a new unchecked exception that will be captured by some other controller, for example the previous task controller. This second controller most probably doesn't care what originated the error but could be interested in some other metadata about time point in time when the exception was raised.

(Guess that implementing the system so that it will be performance friendly while allowing to add metadata about the exception in a genenal is not easy)


Sign in to add a comment
Powered by Google Project Hosting