Let's think about the design of "escape" commands. In general a condition that makes an escape necessary is called an "exception". When an exception is triggered, and control escapes from inside a command, usually some recovery commands should be executed.
Here are some desirable features of exception handling:
Digression/Review: Control Statements
Q: do you remember the type of this statement in Haskell vs. in Pascal...??
case expression of constants_1 : { block_1 } ... constants_n : { block_n } else { block_n+1 } end (*case*) first_statement_after_case // CONTINUE here
Things to remember (Pascal):
switch expression { constants_1 : { block_1 ... break ... } /* OPTIONAL break */ ... constants_n : { block_n } default { block_n+1 } } /* switch */ first_statement_after_case // CONTINUE here
=> C switches violate the single entry, single exit property! (control can flow through several blocks)
Def.: An exception is an unusual event, erroneous or not, that is detectable either by hardware or software and that my require special processing, so-called exception handling. The code unit that does this is called an exception handler. The Ada language [Ada-FAQ] has a successful exception mechanism that meets all these requirements, as do other languages including Java. The mechanisms are very similar, so we'll just look at the Ada syntax. In general exception handlers occur at the end of blocks:
begin block; exception when e_1 => block_1; when e_2 => block_2; ... end;If the exception e_i is raised while executing the block, then execution of the block is abandoned and block_i is executed instead.
If a recovery command is not provided for an exception, then the exception is propagated to the next enclosing block. For example:
begin begin block; exception when e1 => block_1; end; exception when e1 => block_1_1; when e2 => block_2; ... end;In the code above, block_1_1 will never be executed, but block_2 may be.
procedure main is type month is (jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec); rainfall: array (month) of float; negative_rainfall: exception; procedure input_data is begin for amonth in month // implicit declaration of amonth! loop get(rainfall(amonth)); if rainfall(amonth) < 0.0 then raise negative_rainfall exception when data_error => rainfall(amonth) := 0.0; when negative_rainfall => rainfall(amonth) := 0.0; end; end loop; end; begin ... exception when end_error => put("Insufficient data"); when others => put("Unknown catastrophic error"); endSome exceptions defined in Ada packages:
The language Eiffel enforces a rule of good programming style, that the only ways to leave an exception-handler are
The use of exceptions in the Ada example above does not meet the Eiffel criteria.
In general, there are two main opinions on programming style in
the presence of errors. The first attitude is that code should be
written defensively, and as many errors as possible
should be handled, even if they cannot be fixed completely.
The
second attitude is that errors should be revealed as quickly as
possible, so that they can be fixed properly.
For example, according to the first attitude, one should write
for (i = 0; i <= 10; i++) { ... }According to the second attitude one should write
for (i = 0; i != 10; i++) { ... }Suppose that the code inside the loop erroneously modifies i to have a value greater than 10. The first approach will hide this error, while the second will reveal it to the user, by causing an infinite loop.
Here's the general form of these statements:
try { statement(s) } catch (exceptiontype) { statement(s) } ... catch (exceptiontype) { statement(s) } finally { statement(s) } }
A throws clause is used by the compiler to ensure that all checked exceptions are handled appropriately (Error and RunTimeError exceptions are unchecked) by the calling method:
class C { ... void myMethd() throws IOException { // handle elsewhere ... try { ... if (...) throw new MyException(); try { ... } catch (ArrayIndexOutOfBoundsException) { ... } ... } catch(MyException) } }
Continuations.
data BinTree a = MTtree | Leaf a | Node (BinTree a) (BinTree a) data MyList a = Nil | Cons a (MyList a) mybintree = Leaf 1 mylist = Cons mybintree Nil Main> :info mylist mylist :: MyList (BinTree Integer)Can we also have different tree types on the list?
... mybintree2 = Leaf "abc" mylist2 = Cons mybintree2 mylist ERROR "types.hs" (line 14): Type error in application *** Expression : Cons mybintree2 mylist *** Term : mybintree2 *** Type : BinTree [Char] *** Does not match : BinTree Integer
In more traditional programming languages of the Algol family (e.g. Pascal) which have less powerful type systems, one distinguishes between
two variables have compatible types only if they have been declared to be of the same type (based on the name of the type)
here two variables are compatible if they have the same structure