A r t i c l e s
Navigation

Note: This site is
a bit older, personal views
may have changed.

M a i n P a g e

D i r e c t o r y

Try Finally Is a GOTO That Is Too Limited


Where exactly are we GOING

Try and Finally do not allow one to precisely describe WHAT is being tried, and what is being finalized. The "Raise" that occurs, is upstream, so you can't see it in your own code - the raise is hidden in some library.

You may be asking, what about except clauses, don't they label where we are going?
var  number, zero: Integer;
begin    // Try to divide an integer by zero - to raise an exception
  Try
    zero   := 0;
    number := 1 div zero;
    ShowMessage('number / zero = '+IntToStr(number));
  Except
    ShowMessage('Unknown error encountered');
  end;
end;
Delphi programmers discourage except, and encourage "try finally". Exceptions are considered expensive and something not really needed often, because alternatives like TryStrToInt are available instead of StrToInt. Okay so if we are using the alternative without exceptions (TryStrToInt returns a boolean error) then what is the point of having exceptions, if you are going to avoid them? Jokingly, some delphi architect probably came up with TryStrToInt one day and said this:
I like StrToInt, but it causes an exception.. so I am going to invent a better function for dealing with converting strings to integers, using no exceptions! But, errrr, wait, I thought the whole point of delphi was that it had exceptions, and that was one of its advantages.. so, why am I avoiding the use of exceptions and creating a better function, TryStrToInt, when obviously exceptions are superior to error codes? Err... hmm.. maybe exceptions are not all that they are cracked up to be... because errors are, not, um.. exceptional.. but expected to happen in programs? Err wait did I just reinvent the old turbo pascal Val() function... under a new name.. TryStrToInt.... Oh yes I did, because, exceptions are so good that i have to work around exception by inventing new functions that don't use exceptions but return proper errors that I can deal with in the code. Err.. hypocrisy? Oh well, back to coding! Exceptions are so great! Old C programmers who return errors are behind the times. Ugh, but isn't that what TryStrToInt does that I just invented and is now commonly and standard for delphi programmers to use in place of StrToInt...


Even if you do use except (which seems to be actively discouraged), how do you know what code is causing the error, if the "raise exception" is hidden in upstream code (you have to study all the upstream code to know where exceptions occur and to find out all the consequences, and humans don't have time for this).

Goto labels, like used in the Linux kernal by Linus Torvalds allow one to LABEL and DESCRIBE what is happening. I.e. are we trying error 1, or are we trying to exit and free? "Try" keywords are non-descriptive, and Finally keywords are also non-descriptive of what error should be occurring at what time. You do not know what exceptions will be fired off just by looking at a snip of code! You may only know what exceptions will blast you once you run the bad code and see.

Unless you have carefully read the documentation and caught every exception (which just does not happen), it is very unpredictable as to what exceptions will fire off at what time.

Try and finally nests are also not labeled differently, they are all the same try and finally keywords. One can distinguish what is supposedly being tried and finally by looking at the indentation of the nest, but it is not apparent or visible if all the code has been cleaned up for ALL exceptions properly in a given situation. Also, laziness of programmers causes a lot of the code to be placed into a single try finally block instead of deep nests of try finally's. You may get the nests wrong, it is not easy. You may get the "except" block wrong (or you don't know when to use it, as it is extremely confusing).

The keywords "try" and "finally" do not show the programmer what errors are anticipated from what functions.. they sort of appear to make a "guess" of what group of functions could cause errors and "cross fingers hope for the best"! It is never clear as to whether all the code has been checked or cleaned up properly. Even worse, if you put a try finally there, it makes it look as though you have done something right, when in fact you don't know for sure. It gives you a false sense of hope.

In fact, every single function may need a try except or try finally wrapped around it in some way or another, to be safe.. - but this would be verbose - so programmers simply don't bother putting enough try except or try finally blocks in their code, since less is easier.

Try and Finally blocks are a form of a limited GOTO (intraprocedural?) statement, and they force code to be nested in unnatural ways at times (with no labels of what is being GOTO'ed). Try is the same as Try. That's an important bonus of the EVIL GOTO statement.. the fact that one can describe where he is GOING and WHY he is going - we aren't making guesses here, we are specifically routing the code to structured blocks - it couldn't be more structured. Proper use of GOTO statements can act like a CASE or EXIT statement.

Go Out Now

If you have an empty or nil string that someone input as a parameter, you want NOTHING TO DO with the algorithm in many cases. Having ANYTHING to do with the algorithm may cause data corruption - since characters in the nil string cannot be accessed and the algorithm isn't always perfect (we make mistakes in algorithms at times, unanticipated ones like when the character count is 0). Catching the fact that a character count is zero early and skipping past the algorithm is far better than looking into a nested maze of code. and exiting after the nested maze decides that the string has a length of zero, which we already knew anyway).

Structured programmers may argue that nests (even if deep) are always better than GOTO's no matter what.. but the reality is many times in programming we want NOTHING TO DO with bad inputs.. and going through a nest would only cause hazard (in case the nest makes wrong assumptions or in case there is an error in the nest such as an AND not being an OR). The lie that nests are always better than GOTO's are only supported by religious groups.

However, instead of using GOTO, my technique should still rather be implemented as a language feature (a limited goto, like exit and break, but with more benefits). This is a specific technique, and specific techniques should not use extremely flexible controllers like GOTO's.. if possible. Unfortunately, right now, languages don't implement the feature I need, and GOTO is the clearest and cleanest solution. As long as articles and source comments are written clearly, the use of GOTO can be okay while we wait for the proper feature to be implemented for the technique I describe below.

Gotos Harmful?

GOTO's can be removed from languages to stop kids and BASIC programmers from using them - but GOTO's and LABEL's can also be a useful, clean way of exiting functions, if used properly. Ideally, there would instead be built in features to exit a function with a block of code (i.e. an "EXIT with a codeblock that exists" instead of an empty EXIT that does nothing).

We need a limited goto that can only go down, like the exit statement, but with ability to execute a error code block.. no, i'm not talking about exceptions, this is different.. exceptions are upstream "raises" that are like intraprocedrual gotos).

If you think I'm on crack for agreeing to use GOTO statements in some situations, then please read some of Knuth and Wirth's earlier material. Knuth and Wirth generally think unstructured GOTO programming is bad, but RECOMMEND using GOTO statements when used properly. Niklaus Wirth recommended using GOTO statements when they are used for the right situation.

The problem is that lots of people don't use GOTO's properly, so this is why for example Wirth started removing many GOTO features in his languages, and this is why Goto Considered Harmful was written.

Everything Considered Harmful

Nests can be harmful. Casting can be harmful. Reuse can be harmful. Goto can be harmful. Depends how they are used.

Wirth and Knuth are not religiously against GOTO statements, they are religiously against poor use of GOTO statements. Wirth and Knuth recommend using GOTO statements when the GOTO flow goes from top down (i.e. one should try not to jump up, although CONTINUE does).

Using a Good GOTO

procedure algorithm(s: string; num: integer);
var
  local: integer;
label 
  error1, error2, error3;
begin
  if s = '' then goto error1;
  if num = 0 then goto error2;
  
 { important algorithm code goes here }
  //... 
  //... 
      // some nest....
      if local > 60 then goto error3;
  //... 
  //... 

  exit;   { unless there was an error! }

 {----------- keep errors together in a structured area -----------}
  error1: 
    begin
      {$IFDEF VERBOSE_DEBUG}debugln('error 1: string is empty');
      {$ENDIF VERBOSE_DEBUG}

      {$IFDEF DEBUG}debugcode(1);
      {$ENDIF DEBUG}
      exit; // all errors must have a single exit point!
    end;

  error2: 
    begin
      {$IFDEF VERBOSE_DEBUG}debugln('error 2: num parameter must not be zero');
      {$ENDIF VERBOSE_DEBUG}

      {$IFDEF DEBUG}debugcode(2);
      {$ENDIF DEBUG}
      exit;
    end;

  error3: 
    begin
      {$IFDEF VERBOSE_DEBUG} 
       debugln('error 3: 301 redirect recurisive limit of 60 encountered');
      {$ENDIF VERBOSE_DEBUG}

      {$IFDEF DEBUG}  // messy debug IFDEF's don't belong in the algorithm, they can
       debugcode(3);  // be placed here instead. A form of data hiding. One can
      {$ENDIF DEBUG}  // still see this code on his screen below the algorithm 

      exit;
    end;
 {-------------------------------------------------------}
end;
GOTO statements, when used with care, can be more readable than deep nests of if logic or deep nests of try finally blocks.

In the above code, we see that the "IFDEF DEBUG" is verbose and messy.. so using GOTO statements allows the IFDEF messy code to be hidden from the algorithm core code.

The algorithm core code should be kept clean, and should if possible avoid messy IFDEF kludges. Yet, sometimes, in large software programs, we must use conditional IFDEF's for things like extremely verbose debugging. Yet some programs do not require extremely verbose debugging, and only terser debug messages are required.

If those IFDEF conditionals were placed in the core algorithm code, it would be very messy, and the algorithm would become less readable. Yet, a clean and easy solution is to simply move the IFDEF code out into a nice separate structured area.. underneath a label. This, is structured GOTO programming, rather than unstructured GOTO programming.

Z505 Theory: Logging Serious Applications is Critical

Very often, and I mean very very often, in serious applications and serious systems, LOG FILES and AUDIT TRAILS are essential. When do we LOG? Generally, we LOG when things go wrong! But we can't anticipate all things that go wrong, so we use verbose debugging and try and trap the entry and exit of each important function.. so that we can see where the program crashed!

When things go wrong, what do we usually do? Unwind, cleanup, log, and exit as soon as possible, notifying the caller of an error through function results or exceptions. And don't forget, even if I mentioned it already, to log the early exit! Logging the end of the function isn't enough - because exit statements don't hit the end of the function. GOTO statements allow one to exit the function AND log the exit, a big benefit over the more simple EXIT function. One could insert the debug log code in the algorithm instead of using a GOTO, and then EXIT after sending notes to the debug logs.. but this pollutes the source code with debug logs and exit statements and parenthesis and begin/end pairs.. sometimes, GOTO ERROR allows the code to remain much cleaner!

The method I use above with GOTO statements allows us to cleanly log our difficulties and exit the function. I say cleanly log our difficulties because our core algorithm remains clean and readable, while the messy but extremely important DEBUG calls and IFDEF conditionals are stored away from the algorithm (yet, not so far away from the algorithm that it becomes hidden.. rather, one only has to scroll down to the clearly labeled and easy to find code block.. as opposed to the logging being hidden somewhere else upstream or hidden in some internal compiler automatic error message system).

Logging, is extremely useful - and all important systems should be logged. Log files can be analyzed after problems have occurred. Users of software rarely have time to report bugs, and are rarely motivated enough to report bugs. Even programmers themselves have difficulty reporting bugs. But if there is a log file and an audit trail available, it makes it much more easier to see what happened.

Logging also makes it much easier to debug programs that have gone awry and wrong on someone else's computer in some other country, where remote debugging is not feasible or possible (i.e. a user or customer may not allow you to remotely debug a program on his computer or server, and a user or customer may simply be too lazy to offer you this opportunity.. yet if you ask the user or customer for a log file that has already been automatically generated, he will be more than willing to send it to you since it is so easy and convenient to do so).

Unlabeled Try Finally Nests Harder to Understand?

When using:
  if s = '' then goto LOCAL_ERROR1 
It is very logical and clear what is going on here. The LOCAL prefix isn't needed, but it could add further clarity that we are going to a LOCAL label. If the language doesn't allow intraprocedural GOTO's it could be argued that the LOCAL_ prefix is redundant (personally, I'd just use ERROR1 or ERROR_STRING_EMPTY or similar).

With a try finally block, it is not always clear WHAT ERROR we are cleaning up from, because the error we are trapping could be some hidden unknown exception in the upstream code (how do we know what will be raised, when writing new code with an unfamiliar library? an error in a function declaration can be anticipated, so how do you anticipate an exception for un familiar libraries?). This breaks the contract! In order to obey the contract in programming, we must know what we are trapping or what we are doing!

One could add source code comments saying "this try block cleans up all errors such as when the string is empty and when the integer is invalid".. however, not once in my entire programming life have I seen a programmer comment his try finally blocks like this.. plus, these comments become more verbose than just using descriptive Labels.

I think maybe since GOTO has such a bad reputation, programming languages should introduce a specific GOTO statement for ERROR's, just like how BREAK and EXIT and HALT and are specific GOTO statements for breaking out of a loop or procedure or program.

Instead of GOTO.. it could be called EXIT_ERROR or EXITERR, and it could limit the program to only go DOWN rather than UP (i.e. the CONTINUE is an upward GOTO and would not be permitted or part of the EXITERR feature).

Just like how EXIT is limited to exiting a procedure and not traveling upward, the EXITERR feature would limit the programmer to only exit the function with a labeled error.

Nesting Messing

Many programmers throw code into a try finally block, and wonder whether or not there should be some nested try finally's, or whether most of the code should be in all one big try finally block together. GOTO statements naturally allow a procedure to exit in an organized fashion if the programmer knows how to utilize the GOTO properly. If the programmer knows where to GOTO, then he should go there without trying to beat around the bush with nests of if logic or try finally nests. Try and Finally, don't always allow one know when and how he is going to GO, descriptively. What does a try finally say to you? really it says.. "maybe an error will occur, and maybe if it occurs then we will just clean up, but we're not too sure". Whereas an error contract system in the function declaration says: this is the error that you can expect to occur.

GOTO statements, if used with care, are actually easier to understand and code than a nest of Try Finally blocks in some cases. Nesting try/finally blocks is just another way of using GOTO statements without precise labels.

With a goto statement, one can write a GOTO ERROR_XYZ anywhere in the algorithm, meaning no ugly NESTS in many cases. I'm not talking about intra procedural GOTO's (which HALT is) but rather just talking about LOCAL GOTO's. However, even intra procedural GOTO's can be useful - such as HALTING a program from within a procedure.

Halt, or Exit?

In the algorithm at the top of this article, one could even HALT the program instead of using EXIT. This has to be decided by the programmer. For example if a rocket ship is flying into the sky, HALTING the rocket ship engine might not be such a good idea if there is a small error. If the error can be recovered from, it is wise to recover from it and continue on. Exceptions don't offer as obvious ways to recover from errors, since exceptions are not errors? Or are they? (this is where computing science lacks rigor. What is an exception?)

Sometimes we cannot see where the exceptions are going to occur in program code. GOTO statements, if used properly, can show us precisely where our rollback or exit or halt is going to occur. Try finally nests sometimes send us on a wild goose chase, like deep nests do. My point is that sometimes clean and clear GOTO statements are sometimes much more readable than nested try finally blocks.

Is an Exception an Invisible GOTO in Some Cases?

People have trouble deciding whether or not to nest try and finally blocks because they don't know where the errors are going to occur - since the exceptions are hidden away in upstream code. One would have to sift through upstream code in order to find out where all exceptions would occur. Sometimes, the upstream source isn't even available for study (i.e. DLL's or compiled modules), or the source is too large to study. People just throw the code into a try finally block because they don't understand where all exceptions could occur.

Documentation rarely mentions how one is suppose to enclose his code in try finally or except - they leave it up to the creative thinker! Sometimes it is Try/Except, other times it is Try/Finally. API's don't always mention that certain exceptions are going to occur if XYZ activity happens too. Documentation writers make mistakes and skip important details some times. Upstream libraries sometimes assume that they know where exceptions should occur and that everybody downstream is an idiot. Making assumptions isn't such a good thing always. For these reasons, sometimes function results, debug logs, and GOTO statements are cleaner than religiously using exceptions or finally's for everything.

And if you think I'm on crack, then please consider the fact that:

  • If try/finally/catch/raise/except were clear, structured, and if they were the holy grail, there wouldn't be so many disputes and arguments about them

  • Knuth and Wirth recommended the use of GOTO statements in some of their books, they just didn't recommend using GOTO's in bad form

  • Linus Torvalds, Raymond Chen, and Joel Spolsky find exceptions hard to understand... and these are some pretty smart programmers. I don't agree with what everything Linus, Raymond, and Joel say. I agree with a lot of things Joel and Raymond say, and only some things that Linus says.. but we agree that exceptions are very hard to understand at times.

  • Unfortunately, religious Delphi Zealots probably read my wiki.. and I know they just love exceptions. I hate to burst your little bubble. Keep an open mind.

  • exit, halt, and continue are all goto statements.. they are descriptive labels telling the programmer where the function or program is GOING TO.

  • if you were stuck in a maze starving to death, and you were offered a GOTO statement which led you to the end of the maze immediately (and a food stand existed outside of the maze), you would take it without hesitation.

What is Exceptional

It is hard to tell what is exceptional.

For example, if a File Does Not Exist, is this an exception, or is this a logical boolean error that can be handled with an IF statement?

It depends.. if the rocket is going to crash because there is a missing file in the memory drive (or whatever rockets use for data storage) then this might be an exception.. but in other cases, like desktop software.. it isn't an exception. Exceptions, give Upstream library writers too much room for assumption. If we assume it is exceptional that a file does not exist, someone downstream of us may not think this needs to be exceptional.

There is controversy of what an exception is and when it should be used. For example Prof. Andrew P. Black. in his Doctoral Dissertation states that:

"The definitions used by the designers of various exception handling mechanisms are compared, but none is found to be satisfactory."

Proposed Solution for Z505 Debug Technique

What can be done to reduce the use of GOTO statements to cleanly exit and report errors to debug logs? A new language feature must be implemented that is more restrictive than GOTO. For now, many languages do not include this feature.. because no one has thought of it yet. The only current options are to either use GOTO, use upstream somewhat dangerous and hidden exceptions that report errors to somewhere (assumed errors by the original developer rather than anticipated errors by the user-developer), or use messy deep nesting with inline IFDEF and DEBUG garbage that doesn't read well inside the algorithm.

The verbose debugging and IFDEF conditionals for logging belong in a nice separate area that is clearly labeled. This area does not have to be a GOTO LABEL area, it rather can be a restricted EXITERR area that is specifically designed for exiting the function and logging the exit appropriately.

Many programmers ignore debugging and refuse to offer debug logs in their applications simply because current languages make it too hard to exit a function cleanly with debugging on. Debugging applications using writeln('log this') in a clean manner in current languages involves messy {$IFDEF} conditional code obfuscating the core algorithms. The ifdef is to turn debugging on or off at compile time but it pollutes the source with ifdefs. A new language construct is needed if we are to reduce GOTO statements and improve logging ability.

The techniques described in this article by Z505 software are a form of restricted GOTO use, and in order to restrict the GOTO even further, this technique needs to be implemented into the language as a new keyword/construct, just as EXIT, HALT, BREAK, and CONTINUE are restricted forms of GOTO.

Documentation Regarding Exceptions is Confusing

Exceptions are rarely clearly described in documentation, and how to handle exceptions properly is also rarely clearly described. For example, the Delphi documentation shows examples of using Except clauses while in the Delphi newsgroups Except clauses are considered evil, and only Finally clauses are recommended. Other documentation rarely mentions how or what to catch, and how to nest try finally blocks in non-trivial code. In trivial code, try/finally blocks may be simple and straightforward - however programming is rarely trivial in serious systems.

Since documentation generator tools also do not pick up exceptions and explain where they are going to be raised (it would have to know everything about your code), even function error codes may be more clearly documented than exceptions hidden away inside the code blocks of the algorithms. In Delphi, exceptions are sometimes documented but then the except clause is not recommended by several delphi experts and only an unlabeled and non-descriptive try/finally is recommended.

Try/Except offers a label system but yet try except is vigorously discouraged among several programmers - as it is unclear whether a custom exception is a good idea. For example, a Delphi expert, Rudy V. is even unclear himself about whether it is wise to use Try Except.. and says that he is "not sure if it is a good idea" in a newsgroup posting. Many other Delphi experts advise people not to use try except, while others advise programmers to use try except. Not only is the documentation confusing, but the newsgroups and third party advice on exceptions is too.

Since try/finally blocks are not labeling which errors they are catching, this hides a contract, or hides what condition is being tried and what condition is being finalized (one can sort through the code in between the try finally block, but it is not clear how many nests inside that try finally block must be added, nor is it clear how many errors are going to be raised in the try finally block, nor is it clear which functions could be producing the errors that will be sent to the finally block. Basically, the exceptional circumstances are unlabeled, and not described in the code.

Basically exceptions are saying: Out of site, out of mind. Ignorance is bliss.


"Exceptions are only generated in the expectation that they will eventually be handled. For this to be possible, a routine which can generate an exception must declare this fact as part of its specification. Full details of all of the consequences of the exception must be provided if it is to be handled effectively. " --Prof. Andrew P. Black, Doctoral Thesis
(Comment: Andrew is referring to the fact that exceptions should be part of the contract or specification of the function declaration, but they aren't... which means you don't really know anything about the exceptions just by looking at a function declaration. Consider the function StrToInt(). Where does it specify the errors that will occur? only TryStrToInt declares what errors occur since it returns a boolean error as part of the contract. Since exception raises are hidden away in upstream code, there is no visible contract when you use a function and look at the declaration.

Your only hope is the documentation explains all the consequences of the exception and when it will be raised, which is never the case. Documentation just briefly and tersely skims over the exception issue further down the page (not in the function declaration) and gives you a general warning when an exception will occur (glosses over it), but you don't always know when or how it will occur exactly, and there is no official contract! One option is to literally read through all the source code of the library you are using and figure out what and when a raise will occur, which is time consuming, and you probably won't get it right. Exceptions are hard to get right, whereas an error contract makes it obvious that errors will occur. With error contracts you can expect to recover from errors logically, without pretending everything will just get cleaned up and work out okay with a finally block)


"If you have failed the contract, then an unexpected situation has arisen -- it is fundamentally IMPOSSIBLE to program for unexpected situations. Let me restate that: It is impossible to handle unexpected errors!

The only errors that you can handle are EXPECTED errors, and in that case they should be written into the contract. " --Taylor Hutt, Usenet Post

(Comment: Taylor is referring to errors being in the function declaration itself, as the contract, as opposed to hiding them as "raise" in upstream code. If the error is in the function contract (declaration) you can reason about the error. He's also trying to put rigor into computing science by saying, if exceptions are unexpected situations, how can you possibly expect to deal with them? If they are unexpected, you have lost all ability to reason about them. When you expect things to happen, you can deal with it.

Something that is unexpected, is unknown and you can't deal with it - so it is a logical fallacy that unexpected things can be expected (coded for)! Since exceptions are unexpected situations, do they even make any logical sense in programs? It appears not. Computing science has become like astrology or some kind of useless antilogic, with no reasoning ability. Computing science regarding exceptions is a farce. It's not science. This is not good.
)

Another quote:
"Forcing the calling code to handle the error right away is the correct approach, because it forces the programmer to think about the possibility of an error occurring. That's a key point. The fact that this clutters the code with error checking is unfortunate, but it is a small price to pay for correctness of operation. Exceptions tend to allow, even encourage, programmers to ignore the possibility of an error, assuming it will be magically handled by some earlier exception handler."
http://www.lighterra.com/papers/exceptionsharmful/

See also:

Linus Torvalds Goto Statement vs Exceptions Flamewar

Improve-or-Ditch-The-Rude-Exceptions

Suggestion-for-Modern-Pascal-Error-Contracts

Conversation on USENET about exceptions. Many of the replies show how ignorant we were of errors and exceptions back then in 1994. We are about just as ignorant today on the subject. It has become a religion instead of a science or a maths. Where is the scientific rigor on the subject?

About
This site is about programming and other things.
_ _ _