From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,bc243f3bb85ffa4f X-Google-Attributes: gid103376,public From: mfeldman@seas.gwu.edu (Michael Feldman) Subject: Re: Exceptions: Are they GOTOs? Date: 1996/07/13 Message-ID: <4s8jjs$1gu@felix.seas.gwu.edu> X-Deja-AN: 168204791 references: <4s4gic$etl@news.pacifier.com> organization: George Washington University newsgroups: comp.lang.ada Date: 1996-07-13T00:00:00+00:00 List-Id: In article <4s4gic$etl@news.pacifier.com>, Steve Doiel wrote: >In trying to read code, if every possible error condition is checked in line >it may involve nested if's that are hard to read. When exceptions are used >the checks can be made directly where the errors might occur within reusable >components, and the normal flow of code may look very simple. Bingo. Also keep in mind that in Ada, predefined and user-defined exceptions are treated the same way. The _language_ defines the rules of propagation for an unhandled exception - whether it is a predefined or user-defined one. It is important to remember that these propagation rules serve as a standard, well-defined, default flow of control. The programmer can override the default by careful use of exception handlers, but unless he chooses to suppress all checking, (s)he _cannot_ make the rules go away. Constraint_Error (say) _will_ be propagated up the call chain until a handler is found. So the question becomes "how can I best make use of this well-defined capability, for my own abstractions"? There is simply _no_ knee-jerk answer to the question - it is a design issue like all design issues. There are many opinions. Here is mine.:-) > >My opinion: >When code is easier to read is safer and easier to maintain. >Exceptions can make code easier to read. >Therefore exceptions are not bad. > >(Of course you can misuse exceptions like anything else) [Gets on soapbox] I get VERY weary of listening to those who try to stamp everything GOOD or BAD. We have an absolutely outrageous tendency to do that, in an industry where we pompously call ourselves "engineers". [Steps down and gets back to exceptions] 1. By avoiding exceptions, one cheats oneself out of the ability to make a nice visual distinction between "normal" and "abnormal" flow of control. Furthermore, one simply cannot escape the propagation rules discussed above, so one might as well use them to advantage. 2. This leaves us with the problem of defining "normal". I said above that I was giving _opinion_ here, so your mileage may vary.:-) A few examples will sum it up. a. testing for the end of a file by handling Ada.Text_IO.End_Error is _inappropriate_. Why? Every sequential file has an end somewhere; it is NOT an error, or abnormal, to reach it. One should test for end-of-file with the predefined function Ada.Text_IO.End_Of_File. If I _forget_ to write correct code to test for end-of-file, and unexpectedly hit the end of the file, Ada.Text_IO.End_Error _will_ be raised. If that happens, it is because my code has a bug in it - I forgot the test (or wrote it incorrectly). b. Suppose we have an enumeration, say Days: TYPE Days IS (Mon, Tue, Wed, Thu, Fri, Sat, Sun); and given Today, we need to find Tomorrow. Suppose Today happens to be Sun. If we are cycling through the week over and over, Mon follows Sun _every week_; there is nothing abnormal about this. Yet if we naively write Tomorrow := Days'Succ(Today); Constraint_Error will be raised when we "fall off the end of the type." So should we handle Constraint_Error, or just write a special case: IF Today = Days'Last THEN Tomorrow := Days'First; ELSE Tomorrow := Days'Succ(Today); END IF; Handling Constraint_Error here is _inappropriate_; I'd recommend the above IF instead. c. Suppose we have a stack package. Is it "abnormal" to try to pop an empty stack? In most cases, it is. The stack package should - by analogy with the End_Of_File situation in (a) - export _both_ a Boolean function Is_Empty _and_ an exception Empty_Stack. Why? The package should give the client writer the opportunity to do the right thing (check the empty condition before popping), but provide well-defined "guaranteeable" behavior if the client blows it. d. Finally, what is the role of "status codes"? These are very useful anyway, to report various _normal_ results. Consider a table ("database") maintenance package, exporting the usual Insert, Search, Delete operations. Here, an OUT parameter like a Boolean flag Success is useful. Why? Is an unsuccessful search an error? Is it abnormal? Not in my opinion. When you look up a friend's number in the phone book, is it "abnormal" not to find it? No, it merely means your friend's number is unlisted. That is no error; it was quite deliberate.:-) The analogy to our table manager should be clear; it is simply reporting a state, not a "normal" or "abnormal" condition. Indeed, it is the _client_ that must choose the next action. Should I add the item to the database? Or should I treat it as an error because it _should_ already have been there. That's part of the client design, not the package design. In my experience, this last area (d) is the most controversial, with the most variety in designs. There is no substitute for thinking through a consistent, common-sense design that provides the best chance of getting reliable and maintainable code. I can;t give a knee-jerk design here that will satisfy everyone. YMMV.:-) Mike Feldman