The Importance of Exception Handling

It was in another lifetime , one of toil and blood (Bob Dylan) – well, more like over 25 years ago.

I sat through a special course on Pascal and recall making an idiot of myself. I argued that I could do everything in Fortran and what was so special about structured programming. I later rationalised my ignorance on the fact that we did not actually have a computer which could run Pascal. Fortunately, I, probably, was not too moronic because Kesav Nori, who taught us, is still a friend. Later, I used a block structured flavour of Fortran on a DEC10 system and was amazed at how much better my programs read. Presumably, the quality also improved. My personal experience of how hard it was to change is a confirmation of Professor Dijkstra's advice that we in academics have to be very careful about what we teach because it is not possible to unlearn.

We have modernised our curriculum and no longer teach Fortran or, even, Pascal. In general, we insist on teaching C and then we teach C++ or, perhaps, Java. Object oriented programming is regarded as an advanced topic though, in my personal opinion, we are making the students unlearn C and, regrettably, violate the advice of Prof. Dijkstra.

Even after a little programming, a programmer realises that the code (s)he writes for actually solving the problem is a small fraction of the total code. In fact the major part of the code is closely related to error handling. Hence, exception handling is not an advanced topic of an advanced course. It is the basic concept which has to be ingrained in all of us.

The intention of his article is to bring out the essence of the idea so that it may be implemented in any language supporting exception handling. We will use Python as an example because its syntax is closest to pseudo-code; so, people can actually test the trivial programs we use.

If I have to convert and a string to a number, my first reaction is to write what I had originally learnt and practised for many years:

def number(s):

if s.isdigit():

return int(s)

else:

return -1

This is a terrible program. We may have checked for the correctness of input but what are we going to do if even negative numbers are acceptable.

Aside from that, the receiving program will have to check the value returned. For each function called, we will have to check. This is the real tragedy. Pretty soon, the code doing something very simple looks complicated. We can't tell at a glance that all we have done is convert a few strings into numbers.

Let us consider the following code:

def number(s):

value = int(s)

if value < 0:

raise ValueError, “Non-negative only” + s

return value

In this case, the conversion of string to integer will raise an exception if the string contains invalid characters. We, then, check if our string contained a negative number. Notice, this code will not raise an exception if s is '-0'.

Why is this or such code preferable? Bertrand Meyer's book Object-Oriented Software Construction is an excellent source of thought provoking ideas. Simplistically, every function has some preconditions and if these preconditions are satisfied, certain results or post-conditions are satisfied. We have a contract with the method. It should guarantee correctness if the preconditions are satisfied. It should not worry about what the result would be if the preconditions are not satisfied because that is a violation of the contract.

Our method is perfectly correct in throwing up its hands and pointing out that the preconditions are not satisfied. It raises an alert for whichever module can handle this condition correctly, to do so.

Suppose we wanted to give a prize to the oldest couple and define the age of a couple as the sum of the ages of the husband and wife. The code fragment may look something like

def getData():

return raw_input(“Enter Husband's age:”), raw_input(“Enter Wife's age:”)


# Main program starts here

try:

h_age, w_age = getData()

c_age=number(h_age) + number(w_age)

except ValueError,s:

print “Wrong Data”, S

A function returning two values is not regarded as a good programming practice; but getData should be viewed as returning a tuple or a list of related values.

I will leave it as an exercise for the readers to write the code for the sum of two calls to 'number' using the earlier C-style version. An obvious suggestion would be that we have structured our code inappropriately. We should ensure that only correct data is accepted. Then, we will not need to check for an error in 'number' and our C-style code will be equally clear.

This is correct; however, in even moderately complex methods, validations and error conditions inside the functions have to be handled. We are illustrating the potential complexity using a trivial example, which would normally be written differently.

Let us consider that the data is already entered and available from a file with comma separated fields. Our main program reads the data and then calls a program to find the winner.

def selectWinner(dataList):

maxAge = 0

for rec in dataList:

# Assume that the first and second fields contain the two desired ages

sumAge = number(rec[1]) + number(rec[2])

if sumAge > maxAge:

winning = rec

maxAge = sumAge

return winning


# The main program starts here

couplesData=[] # start with an empty list

try:

for rec in open('csv.txt','r'): #iterate over the file

# Ignore the last character of the string – rec[:-1].

# This is the new line character.

# Next, split the fields using ',' as the delimiter

couplesData.append(rec[:-1].split(','))

winner = selectWinner(couplesData)

except IOError:

print “Unexpected error in reading file”

except ValueError, s:

print “Data Error”, s

else: # in case there is no exception

print “The winner is “, winner

The key point to notice in the 'selectWinner' function is that although the method 'number' may return an exception, selectWinner completely ignores that possibility. If the list of data is not proper, it is up to the program which called it to sort out the mess. This is the great simplifier. We insert exception handling code only where we know how to handle the exception.

We will end with rewriting selectWinner using functional programming concepts.

def selectWinner(dataList):

sumAges =map(lambda rec: number(rec[1]) +

number(rec[2]), dataList)

maxAge = max(sumAges)

return dataList[sumAges.index(maxAge)]


The first statement creates a new list, sumAges, which maps each entry to the sum of the two ages. In the next statement, we find the maximum value. Finally, we find the index in sumAges where the maximum value occurs and use that to return the desired record. Without using exception handling, enforcement of the contracts will be very difficult.

By the way, a second couple which had the same age as the winner has just gone to court, complaining - “Unfair program!”

Comments