Monday, August 11, 2008

Exceptions, return value checking, pre- and postconditions

I never actually worked with Eiffel but when I first heard about it (that is some decades ago, really...) there is one concept that I remembered for the whole of my professional career and I adhered as a 'coding principle' in my own code. I'm talking about the concept of pre- and postconditions.

As I remember it in Eiffel you can write your method/function/procedure (whatever it's called) and define some conditions that must be met before and at the end of that. E.g. you could define a precondition for a function that will ensure that a passed parameter is larger than zero. For the remainder of your code you can assume that the passed parameter IS larger than zero.

Now, whenever I write a function/method I do the same. E.g. if I do a typecast somewhere in my code, I try to check for the correctness of the object that is typecast as soon as possible in my code. Something like this (from the top of my head, forgive me typos, simplicity of the examples and such):

procedure Foo(const aControl: TWinControl);
begin
// Beginning of preconditions
if not Assigned(aControl) then exit;
if not (aControl is TEdit) then exit;
// End of preconditions

// You can now safely execute the code below
TEdit(aControl).Text := 'foo';
end;

The above works fine if there actually is something that you can check at the beginning of the code. But, what if you cannot, until right before you start to use an object? Like below:

procedure Foo();
var
aControl: TEdit;
begin
(**
A Whole Bunch of Code Here
**)
aControl := MyFunctionSomeWhereThatReturnsTEdit();
if (not Assigned(aControl)) then exit;
if (not (aControl is TEdit)) then exit;
aControl.Text := 'foo';
end;

In this case you would add your check just below the aControl := line. And if you have a lot of those, your code can become complex and hard to read. And you would end up with a lot of not so interesting code which in the end doesn't really matter to your program.

Now, until now I used assertions for this type of checking, mainly because you can turn those off easily using a compiler switch.

aControl := MyFunctionSomeWhereThatReturnsTEdit();
Assert(Assigned(aControl));
Assert(aControl is TEdit);

But, of course, this doesn't work if the 'failing conditions' might be ligitimate circumstances in your code. So, for those types of checks I used the if..then constructions from the first example.

But, I am now thinking that I'd better use exceptions for this. Currenlty I only use exceptions for 'error circumstances' and never really used them as a way to signal pre- and postconditions. It would go like this:

procedure Foo();
var
aControl: TEdit;
begin

try
(**
A Whole Bunch of Code Here
**)
aControl := MyFunctionSomeWhereThatReturnsTEdit();
aControl.Text := 'foo';

except
on E: MyConditionException do
begin
// This will be an exception raised from within MyFunctionSomeWhereThatReturnsTEdit
end;
on E: Exception do // There probably are seperate exceptions for nil and class type mismatch exceptions, but this will do for the example
begin
// Something else should be done.
end;
end;
end;


This will work (and I think I will give it a go in some small program to see if it works our for my style of programming), but I'd much rather see the concept of pre- and postconditions introduced in Delphi.

What do you think? If..Then constructions at the beginning? Exceptions? Push Embarcadero for pre-/postconditions? Something else?

Please let me know; I'm really curious.

Bye,
Bart

6 comments:

Anonymous said...

Pre- and Post-Conditions are part of RemObjects Chrome. I think it would be great if CodeGear/Embarcacantspellit would simply use their syntax.

But until then Exceptions are the way to go. Exit is a bad solution because it won't tell you that it failed. I usually do something like this:

if Param1 < 0 then raise ArgumentException.Create('Param1 must be positive', 'Param1');


For Win32 code I defined Exceptions that behave like the .Net ones (http://www.daniellehmann.net/Net4Native/).

Lars Fosdal said...

Avoid the mistake, or repair the damages?

Personally, I try to stick with Asserts as my design-by-contract pre/post/return condition police mechanisms. Their primary use is to point out my obvious mistakes during the development and testing phase.

Assertions are meant to go boom if I didn't stick to the plan. If it didn't go boom during development/testing - but did go boom in production - chances are that I didn't make sure that the parameters met the requirements of the routine at time when I called it. I broke the contract.

Exception handlers are of a different nature. They are intended to allow us to recover gracefully from things that blew up, either by accident or by design (ie using raise).

IMO, if our code don't rely on external factors or code beyond our control and don't explicitly raise exceptions, we should not really need exception handlers in that code.

If we introduce exception handlers, it should be when
- we don't have control over the code calling us
- we don't have control the quality of the data that is passed to us
- we don't have safe methods to check of the above two points

Exception handling is costly, so you should avoid it if you can in performance sensitive code (tight loops). If I can, I prefer to write code where I can pass on the results (ie old API style) rather than using exceptions, but exceptions are very handy in event oriented code.

Pre/Post-conditions: Personally, I don't want Pascal to turn into Ada, although I would not mind such functionality as long as it is optional. If you need the mechanism, you can still implement it "manually", by using asserts, virtual methods, or simply define local functions within the method. RemObjects Chrome's require and ensure (http://wiki.remobjects.com/wiki/Class_Contracts) are not really much more than syntactic sugar for assert and try/finally.

Anonymous said...

I will use exceptions only for raising errors with only error logic in the exception area, for all other cases I would test and use if or similar flow control statements.

Hope my begin and ends Match Up...

ex:
procedure foo();
var
vEdit: TEdit;
begin
(* Some code *)
try
vEdit:= MyTEditReturningFunction;
if Assigned(vEdit )then
begin
vEdit.Text := 'Foo';
(*SomeCode with vEdit*)
end
else
begin
(*Handle Valid unassigned vEdit or Handle/Raise Unassigned Error*)
end;
except
on E: EMyException do
begin
end;
on E:Exception do
begin
(* Other Exception Handling Code *)
end;
end;
end;

end;

I had to debug some "strange erratic errors" in code where a previous developer had a lot of logic in the except statement.
I eventually refactored the exception out into a couple of if statements. This in turn allowed much needed refining of the user interface and addition of much needed functionality.

Anonymous said...

We should be careful before dismissing the use of EXIT.

To my mind EXIT and exceptions serve two very different but equally useful purposes.

1. A call has been made which is unexpected and does not make sense.

This calls for an exception (whether that is an ASSERT or not depends on whether the condition might reasonably arise during use, or is an out-and-out developer cock-up)

2. A call has been made for which this method has no behaviour.

This is a simple control flow decision - "you call me like this, I do nothing".

In this case, we could enclose quite possible the entire body of the method in one all encompassing "if then begin .. end" or we can catch the condition early and quietly exit the method.

This leaves the rest of the method - the normal body of code - much easier to follow.

The Pascal version of FreeAndNIL is the usual example I offer of these "null-op" type conditions:

if NOT Assigned(self) then
EXIT;


If I ever find myself considering an EXIT too far into the "meat" of a method body this usually indicates that the method is trying to do too much.

As soon as a method has more than 2 "main flows", with or without null-op conditions, it is usually sign that what I really have is two or more methods, masquerading as one.


I too like the Chrome.. oops... make that Oxygene... pre and post conditions, but they don't (currently anyway) support null-op conditions, only errors.

Anonymous said...

Exit can be used to exit a function (obviously :) and I consider that good style if properly used) but it is not a way to implement pre-conditions. Because if the function properly exits without error, no pre-condition was violated.

About Assert and Exception I am not really sure which one is better. In the end, Asserts are also just exceptions which can be disabled by compiler magic and are supposed to not be caught. But I would never catch an exception like ArgumentException or OutOfRangeException either.

Craig said...

Bart,

Per Bertrand Meyer's definition of the term, anyway, the lines ending in "Exit" are not preconditions. They're just flow control. Not that there's anything wrong with that, but let's be a little more rigorous about terminology. So before I discuss your actual question, I want to talk about what some of these words mean.

The purpose of a precondition is to detect violations of the method's contract. The only correct response to a precondition violation is to raise an exception. However, if the method's contract does allow, in this example, something which isn't a TEdit, then that is not a precondition violation, and no exception should be raised.

Let me give you an example from Meyer. If you are doing input validation — say, you have asked the user to enter a number — then the contract of the method doing the validation should allow for erroneous input, since users make mistakes. Hence, it is inappropriate to raise an exception when the user enters something which cannot be parsed into a number, since the contract allows for this. Instead, the method would return a value indicating that the user has entered invalid input.

So let's return to your example. First, it sounds to me as though the contract of MyFunctionSomeWhereThatReturnsTEdit is that the return value should always be a TEdit. (If it isn't, it's a horribly misnamed function!) So you should probably have a postcondition inside that method which checks for this. If this is the case, then you have no need to test its return value in the middle of Foo, since that is already being tested elsewhere.

On the other hand, what if MyFunctionSomeWhereThatReturnsTEdit was actually misnamed and this method might not return a TEdit? in this case, Foo cannot presume the type of the return value. Hence, the tests midline are appropriate.