3

Many people consider exceptions to be a problem because they create invisible paths through your code. For example in this snippet:

function writeToFile(text, filename):
  filehandle = open(filename)
  foreach line in text:
    filehandle.write(line)
  filehandle.close()

The write method could throw an Exception. In that case the function will end prematurely and the filehandle is not closed. Writing exception safe code is hard.

On the other hand adding explicit error handling like in C clutters up the code considerably. (This seems to be the "other" option if you don't like exceptions.)

function writeToFile(text, filename):
  filehandle = open(filename)
  if nullptr == filehandle:
    return
  foreach line in text:
    if(!filehandle.write(line)):
      filehandle.close()
      return
  filehandle.close()

Now this is just a simple example. I just wrote code to do some logging and between the abstract concept of "Just dump data into a file!" and my implementation is a difference of about 200 lines.

So what do people do to keep their code readable in spite of all those corner cases that they need to watch out for and handle?

Do we have to resort to literate programming? :)

0

4 Answers 4

4

In a language that supports deterministic destruction:

function writeToFile(text, filename):
  filehandle = open(filename)
  foreach line in text:
    filehandle.write(line)

and the type of filehandle just closes itself in the destructor. This is how it works in C++.

Languages without deterministic destruction often have syntactic constructs for scoped resources: using-block in C#, try-with-resources in Java, with-block in Python:

function writeToFile(text, filename):
  with open(filename) as filehandle:
    foreach line in text:
      filehandle.write(line)

If they don't (Java <7), there will be something like a try...finally construct instead. And if that doesn't exist either, don't use that broken language.

Languages with a focus on closures (Ruby, most functional languages, like Haskell) often use the around-pattern instead - sorry for abandoning the python-like syntax here:

function writeToFile(text, filename) {
  withOpenFile(filename, lambda(filehandle) {
    foreach line in text {
      filehandle.write(line)
    }
  })
}

Bottom line is that you want some support from the language for ensuring that some code is executed no matter how you leave a block.

Keep in mind that what this is talking about is not really error-handling. This is about exception-safety: code doing the right thing when an exception is thrown. You still have to actually deal with the exception somewhere, but this is not about that.

1

I don't see anything wrong with code such as the second example. The details could probably be improved a little bit, but, yes, a lot of code ends up being error handling, data verification, and the like. This is essential to writing good code.

It would be a mistake to view it as "extra stuff" that gets in the way of the real work. Such a mindset is likely to lead to inadequate error checking.

I once worked on some aviation software in which the core algorithm probably could have been written in about 50 lines. This was surrounded by thousands of lines that existed primarily for error checking and correction. This may be annoying, but it is rather essential for an function like keeping a plane in the air.

2
  • 1
    I totally agree with you that proper error handling is important but for understanding large codebases you need some sort of abstract view of the behavior of the system and I think error handling obscures that view.
    – Sarien
    Commented Jun 5, 2013 at 12:21
  • @Sarien, I think the main answer to that is good design principles like abstraction and encapsulation.
    – user82096
    Commented Jun 5, 2013 at 12:34
0
function writeToFile(text, filename):
  filehandle = open(filename)
  if (filehandle)
  {
    foreach line in text:
      if(!filehandle.write(line)):
        break;

    filehandle.close()
  }

that's not so bad, and does everything you wanted, with error handling.

Or:

function writeToFile(text, filename):
  filehandle = open(filename)
  try
  {
    foreach line in text:
      filehandle.write(line)
  }
  catch (...)
  {
  }
  filehandle.close()

again, not too bad and would be better in a larger function.

I think the issue here is not so much error handling, which is an important and necessary part of coding, as much a part of coding and the 'functional' code, but the mindset that says it needs to be hidden or that the error handling code is somehow a dirty secret that has to be kept out of sight. You have to put lots of other stuff in there (eg logging) that will bloat your code up.

Perhaps the best approach for you is to use C/C++ and use macros to keep your "extraneous" code to single-line statements. Or just to learn to love error handling code.

3
  • I agree with you but my original problem is more abstract. For example: Ask somebody what a prime is. They will probably tell you "a number that is only divisible by one and itself". Seems fine. If pressed they would probably remember that 1 isn't prime even though it fits that definition. But that is a detail that is better omitted for a coarse look at things. I think error handling code is the same. Important but clutter and noise when it comes to actual understanding of things.
    – Sarien
    Commented Jun 5, 2013 at 12:24
  • Both are bad in that they silently mask errors, so the caller can't react to errors.
    – user7043
    Commented Jun 5, 2013 at 12:27
  • A prime is a number that has exactly two divisors :-) Commented Jun 5, 2013 at 12:46
0

No doubt about it, clean error handling is hard without cluttering up code. Although not ideal, I try to put a try catch block in the base function I am calling. For example, if I have a basic console application that is set up as follows (not meant to be language specific, just an example):

Main() { myFunction(); };

myFunction() { // do stuff };

putting a try catch block inside main will catch any errors that occur in myFunction. Regretfully this is not always guaranteed to work for every style of program, and will present difficulties when dealing with code that does not get called from the same location (in the above example Main). For languages like javascript, where you can pass a function as a parameter, you can write a simple wrapper function that simply calls a function that was passed to it inside a try catch block and returns the result.

Where and how to gracefully catch errors is also partly dependent on how your program logic is set up. If most of the code that could produce errors is located in a separate class it could be better to handle the error in the class itself and not worry about throwing it to the caller.

In general, I try to put a try catch block in my base functions (such as Main in the example), and an additional try catch block anywhere that actions need to be taken on error, such as closing a file reader. For the most part that will catch most errors that occur (once again depending on how the code executes), and will provide additional security when needed.

Not the answer you're looking for? Browse other questions tagged or ask your own question.