Sunday, July 18, 2010

Exceptional Exceptions

If you're like me there's a good chance you learned programming using a language that didn't have the concept of exceptions. Instead, you probably had some error parameter to check to see if the call you just made succeeded. And if you're like me, you've probably heard that the REALbasic language has a feature called "exceptions" but you don't know much about them. You may know that they happen and that when they do it means something has gone wrong, but that's about it. Until quite recently, that was me. I didn't mess with exceptions because I didn't fully understand them and using an IF statement to check for an error was somehow more familiar and comforting.

In an effort to make the REALbasic framework more self-consistent, we are deprecating a few functions which are going to require you and me to get familiar and comfortable with exceptions. If the word "deprecation" is unfamiliar to you, it means we are not longer going to support a particular feature and will, one day far in the future, remove the feature. However, we do remove the deprecated feature from the documentation immediately to discourage its use. Why would we do this? Because if we don't, the REALbasic framework will age and be filled with "cruft" as we call it. Cruft is extra, unnecessary stuff that we have to maintain and thus adds to the expense of bringing REAL Studio to you. It also confuses new users as they aren't sure whether method A or B is the right one. If we don't deprecate things in favor of a better way of doing something, one day we could be in real trouble when some technology we have all grown to depend on suddenly doesn't work anymore and we require you to make massive changes to your code to move it forward. So think of deprecation as being like the pain of regular exercise. It's necessary, not always fun but better than the consequences of a lifetime of neglect.

So back to why you need to learn about exceptions and why they can actually be fun and rewarding. In REAL Studio 2010 Release 3 (the next release of REAL Studio) we are deprecating the NewPicture function along with NewMemoryBlock and NewAppleEvent. There are two reasons for this. First, these functions are not consistent with the regular way you create objects. For example, to create a new date object, you use this syntax:

Dim Today as New Date

Wheras with the NewPicture function, for example, your syntax would be:

Dim p as Picture = NewPicture(100, 100, 32) //the parameters are width, height and depth

The difference here appears to be nothing more than the space between "New" and "Picture". After all, this syntax is also valid:

Dim p as New Picture(100, 100, 32)

But in this case, you will need to use an exception. More on that in a moment.

The second reason we are deprecating these functions is because of how you deal with them when things go wrong. NewPicture (along with NewMemoryBlock and NewAppleEvent) creates something and returns the object if the object was created successfully or nil if it was not. When these functions return an object, life is good. When they return false however, the reason for that failure is unknown. Your guess is as good as mine.

Not with exceptions however. Exceptions give you a way to know exactly why an object was not created and they don't require any more code than an IF statement. In fact, they can sometimes require LESS code. Let's consider the following Canvas Paint event example where we create two pictures using NewPicture and then draw one into the other , then draw the result into the canvas:

This example has 14 lines of actual code. The two IF statements that check to see if the object was created or not make the code more complex and harder to read. Here is the same code rewritten using an exception:

Using exceptions, this code is only 10 lines and it a lot cleaner and easier to read. The catch statement at the end makes it quite clear why the pictures could not be created so your code is more self-documenting and readable. And of course, you could trap for other possible reasons as well.

Now, in the exceptions example I'm only dealing with the fact that either picture could not be created. But in this example, if I can't create one of them, then there's no need to continue. The reason exceptions are called "exceptions" is that they are not likely to happen. If they do, that case is an exception. Hence the name. In the first example, I have to have an IF statement after each attempt to create a picture. If I don't my app will crash with a NilObjectException. Yes, an exception. So you are dealing with them anyway. But in the second example, using an exception, I don't need to do this. If one of the pictures cannot be created, the code will immediately move down to the Catch statement.

Finally, if you have some code that should execute even if an exception has occurred, you can add the Finally statement after Catch but before End Try. You can see an example of this here.

Now you may think you've got a better way to write my examples above and you probably do if you think that. After all, I'm the CEO and that "E" doesn't mean Engineer. But the point is, exceptions are either the same amount of code or less than using IF statements and make your code easier to read and more self-documenting. They are a good thing. Do you have a choice to use them or not? For now, yes. But some day, a long time from now, NewPicture, NewMemoryBlock and NewAppleEvent will be removed entirely and then you will have to use the New keyword and exceptions so why not start today?

Think of this as an opportunity to learn something new and sharpen your skills. If you need some extra, added incentive, many other object-oriented programming languages support exceptions as well. Not that you will ever need to use any other language of course.

8 comments:

Anonymous said...

Nice post Geoff, as was your last one. I learned something from both.

Anonymous said...

As long as exceptions are only thrown in those specific conditions when very unlikely events occur, then I think it is okay, but otherwise I really dislike exceptions because they "jump" the code flow. I hate the idea of putting try...catch everywhere.

George said...

There is a problem with this.

dim tmp as Picture

tmp = new picture(1,2,3)

gets you no prompting as to what 1,2 and 3 are for.

Whereas

dim tmp as new Picture

tmp = newpicture(1,2,3)

gets you the autoprompting for what 1,2,3 are as its a function.

At least it does on my version of RB (I have chosen to stick with the last version of RB that doesn't require activation) so maybe its better now.

Michael

Geoff Perlman said...

George,

The correct syntax is:

Dim tmp as New Picture(1,2,3)

3 isn't valid as the third parameter but I'm sure you know that. If you type in this syntax, when you get to picture, the status bar will give you the correct syntax. I don't know which version you are using but as of 2010 R2 you do get the syntax.

Geoff Perlman said...

Sorry, it said "George said..." but you message was signed Michael.

Anonymous said...

Geoff,

While this may sound like I'm being picky, I don't want people confused.

The two syntaxes:

Dim X as New Object

And

Dim X as New Obj
X= New Object

Are EQUALLY correct and will aways be.

The first is a convenient shorthand for SOME cases

If you are creating objects in a loop you may not want a new variable every time.

You may be doing something with a an object locally and then passing it to method in a single pass.

Or you may be assigning objects to an array...

Or you may be Diming multiple objects variables at the same time.

Or some may just prefer it...

In any case it's in no way incorrect.

charles said...

Although the two

Dim X as New Object

and

Dim X as Obj
X= New Object

are both correct, I claim that the former is almost always the better choice.

charles said...

I am pleased to see that REALbasic might be moving more in the direction of using exceptions for error-handling. Three things come to mind.

Unlike the return type of a function, the compiler doesn't know what exceptions might be raised from a function. Thus it is critical that the use of exceptions be documented. And it would be useful for framework code to include information when possible. An OutOfMemoryException raised by Picture.Constructor is already clear. But there are other places in the Rb framework where this is not the case. For example, methods that raise an UnsupportedFormatException could well include something in the exception's Message property.

I disagree with the idea that exceptions should only represent "exceptional conditions". Such a view leads to an error-handling mess in which some errors might be represented by error codes, and others by exceptions. REALbasic contains a substantial amount of such cruft.

I don't find the use of try blocks and exception handlers to be any more onerous than heavy use of if blocks. The ability to separate error-handling code from what might be called error-generating code means that I can do a much better job of testing error handling, and eliminate code duplication.

The fact is that error-handling is difficult and a lot of work; it is one of the things that separates professionals from amateurs. Having to deal with both error codes and exceptions (e.g. FolderItem) only complicates my job.

However, it is important to remember that exceptions come with their own difficulties. An excellent summary is given in Raymond Chen's article Cleaner, more elegant, and harder to recognize.