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: gauthier@unilim.fr (Michel Gauthier) Subject: Re: Exceptions: Are they GOTOs? Date: 1996/07/17 Message-ID: X-Deja-AN: 168584498 references: organization: Universite de Limoges newsgroups: comp.lang.ada Date: 1996-07-17T00:00:00+00:00 List-Id: This is a reply to a private message following a c.l.a message in which , brad@cs.uwa.oz.au (Bradley Edelman) wrote: >>> What do people think about exception handling? I believe it to be a major >>> strength of Ada but there is a school of thought that exceptions are just >>> GOTO statements and should be avoided accordingly. The following ideas are milestones, rather than a well-built paper. "resuming requirement" : some projects require appropriate reactions after any situation, including bad ones. Other projects may locally encounter the same requirement (basic case : syntax errors in typing input data). "reuse principle 1" : a reusable component is not allowed to make assumptions about its uses. Consequently : it is not allowed to assume anything about 'normality' or 'abnormality' of calls. The only things a reusable component may do are rendering the service it has to, or reporting that it cannot. Consequence : aborts and goto's are not suitable to report, since they assume too much things about the use. Known vocabulary : programming is 'offensive' when the caller has to check some precondition that permits the callee to work correctly, and 'defensive' when the callee checks the preconditions and reports accordingly if it cannot work correctly. 'Offensiveness' is a property of each caller. 'Defensiveness' is a property of the callee. Some calls can be both. Consequence : the 'defensive' behaviour is the only possible one for a reusable component, since the contrary assumes that all callers are 'offensive'. Of course, there is no harm in non-defensive behaviour when the programmer has a full control on a well-defined set of calls. Known implementations of reports : exceptions and status codes. Balance : status codes imply verbosity caused by the requirement of explicit handling everywhere in the program, exceptions imply implicit information, which has to be made explicit by re-building or by appropriate documentation. IMHO : exceptions are simpler in most cases, not necessarily in all cases (remind that we speak of reusable components only). To convince yourself, consider what you would have to write if Text_IO was specified with status codes, and the quality of the resulting programs. Complexity of goto's : very long ago, I wrote a paper about measuring (in McCabe's style) the effect of transforming non-'structured' algorithms into 'structured' ones. More generally, it may be interesting to measure the effect of program-transforming algorithms. About goto's, the result was : - additional complexity of forward-goto's : the number of control structures that the jump exits from - additional complexity of one backward goto : the above, plus an enclosing loop - additional complexity of mixed forward and backward goto's : not computable, since the transformation involves copying pieces of source text. According to this idea, exception raise and handling is exactly like forward-goto's (with any language, except Eiffel's 'retry'). Be aware that this complexity _is_ in the program, whether you handle exceptionsor not. Note : this seems to illustrate that exceptions are less complex than status codes, which work as transformation of exceptions into 'structured' code (status introduces flows that converge, goes together during a time, and then diverge again). Ambiguity of 'contract' : the idea that a specification is a sort of contract (in the commercial sense) between a client and a provider is a very old idea. I remember that I, and other colleagues, was already using it in my courses (using Pascal and status) in 1975. However, it is ambiguous whether the word 'contract' denotes the expected service in a limited sense, or the behaviour in all cases. The renowned (but IMHO somewhat weak) Bertrand Meyer's book is ambiguous too. There are two concepts, we need two words. _Remind that_ : almost every statement and many declarations can raise at least one exception. Languages that do not provide such control are not dependable. Suggestion : call 'specification' the definition of the basic service, and 'contact' the full definition with report cases. Also call 'success' the case of obtaining the specification, and 'failure' the contrary. Do not call of 'errors' here. Remember that preconditions can sometimes not be coded as predicate functions : opening a remote file (can be, but implies a non-atomic statement), inversing a matrix (same complexity as the basic computation), getting a number from the user (intrinsically impossible),... The issue of numeric compotations and overflowing is intrinsically impossible, too. Of course, the so-called 'contract model' of genericity is compatible with this choice of words. Very important : "success vs. failure" / "correct vs. erroneous" / "exception vs. status" are entirely independent distinctions : you can find examples of the eight possible cases (although one of the eight is somewhat exotic, but sometimes required). Particularly : yileding a result when a report is required by the specification is an error !!! "raising is simple" : in most cases, you are inside one or more branches of conditional statements (or cases, or loops), which builds a rich knowledge of the program state, and permits you decide that it is a situation where the contract requires a raise. The same holds with status codes, and goto'ing is similar. "handling can be complex" : a handler is a location where many flows converge. There are many risks of errors here : - if your mind omits one corresponding raise, - if you have not enough information about the program state at the locations of the raises. Useful rules can be deduced from this : - compute the exact set of raises, - prove appropriately if some raises cannot be reached, - attach postconditions to exception raise locations as well as to any other subprogram exit. There is, of course, always the risk of bad proofs, but nothing will never decrease this risk to zero. Caution for status codes : the equivalent (with exceptions) of if REPORT /= SOME_REPORT then SUCC ; else FAIL ; end if ; will include something like : when SOME_REPORT => FAIL ; when others => -- ??? SUCC ; -- ??? Dangerous, isn't it ? Yet more rules : in fact, a good use of reports requires the above rules for _all_ reports, whether reporting is by exceptions or by status codes. Hence, for a piece of program (say, subprogram) : - consider _all_ raises, including implicit ones, or assignments of the status, - remove all non-reachable raises of this set, after proving it, - consider all reports allowed in the contact, - check if internal effective raises and final reports fit, - every non-fit detects a programming error. Sorry, but : without such style rules, how can you depend on a program ? ---------- ---------- ---------- ---------- Michel Gauthier / Laboratoire d'informatique 123 avenue Albert Thomas / F-87060 Limoges telephone +33 () 55457335 [or ~ 7232] fax +33 () 55457315 [or ~7201] ---------- ---------- ---------- ---------- La grande equation de la fin du siecle : windows-X = Mac-Y The main end-of-century equation : windows-X = Mac-Y ---------- ---------- ---------- ---------- Si l'an 2000 est pour vous un mysticisme stupide, utilisez la base 9 If you feel year 2000 a stupid mystic craze, use numeration base 9 ---------- ---------- ---------- ----------