CSE130 LECTURE NOTES


November 15, 1999
 
 

ADMINISTRATION

We're returning the second project today.

Reminder: Plagiarism will not be tolerated.
 
 

OOP KEYWORDS

(1) Object identity. Even if two objects have the same internal value, they are still different objects.  Similarly, two variables are different even if temporarily they have the same value, because they have different locations, and hence in the future they can be updated differently.

Object identity is connected to an "object-centric" view of programming: every operation is about some primary object.  Syntactically, every method has an unwritten (implicit) first argument, which is the object itself.

(2) Encapsulation. The state of an object is private to the object: the only way the state can be changed or observed is by calling one of the object's methods. This is what we called "implementation-hiding" before.

(3) Overloading. Two objects of different classes can have a method with the same name. If these methods perform conceptually similar operations, the caller of the method does not need to know the exact class of the called object.  Overloading is particularly important with inheritance.
 
 

OWN VARIABLES REVISITED

Another point of view on objects is that an object is a single procedure with some variables that are global and private, i.e. they keep their values between calls to the procedure but they can only be updated inside the procedure.  The methods of the object are alternative instructions that the object can execute.

According to this view, the object itself is really a dispatcher: it waits for a message and then selects one of its methods for execution based on which message was received.  A class is a function that can generate new objects.  For example:

type messages = (next, restart);

function newgenerator (initial:real) =
begin
    var seed: real := initial;
    return
        function (m: messages) =
        begin
            case m of
                next: seed := transform(seed);
                restart: seed := initial;
            end;
            return seed;
        end;
end;

function myrandom = newgenerator(1.2345);
function hisrandom = newgenerator(0.1428);

Here newgenerator is a higher-order function that returns another function, which is anonymous. Each returned function is a pseudo-random number generator (PRNG) that can respond to two alternative messages.

The updated PRNG seed is kept in the variable seed which is local to newgenerator so seed is fresh each time that newgenerator is called.  However seed is global to the anonymous function so each version of seed keeps its value between calls to the corresponding individual PRNG.
 
 

ESCAPE COMMANDS: EXCEPTIONS

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:

(0) The single entry, single exit property should be preserved: that control can only flow into the command at one point, and can only flow out at one point.

(1) It should be possible to raise exceptions and recover from them for any command, not just loops. There can be reasons to abort any command.

(2) When an exception is handled, it should be possible to execute some recovery commands as an alternative to the regular command which gave rise to the failure. For example if an exception is raised while reading an input value, it should be possible to recover by assigning a default value.

(3) It should be possible to provide exception handlers for many different types of exception.

(4) Providing exception handlers should be optional. If a handler is not provided, the exception should cause an escape from the enclosing block.

(5) Programmer-defined exceptions like "negative age" should be allowed in addition to system-defined exceptions like "division by zero".
 
 

EXCEPTIONS IN ADA

The Ada language has a successful exception mechanism that meets all the requirements above, as do ML and 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
    C;
exception
    when e1 => C1;
    when e2 => C2;
    ...
end;
If the exception "ei" is raised while executing "C", then "C" is abandoned and "Ci" 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
        C;
    exception
        when e1 => C1;
    end;
exception
    when e1 => C1';
    when e2 => C2;
    ...
end;
In the code above, C1' will never be executed, but C2 may be.
 
 

THE "EIFFEL" WAY TO USE EXCEPTIONS

Exceptions should not be used in place of regular testing for alternative cases, e.g. for leap years in software that processes dates.

The language Eiffel enforces a rule of good programming style: the only ways to leave an exception-handler should be (1) to execute the failed operation again, or (2) to propagate the exception.

With re-execution (called "retry"), after raising e1 and executing C1, C would be executed again.

With propagation (called "re-raise"), after raising e1 and executing C1, C1' would then be executed.
 
 

A REALISTIC EXAMPLE OF EXCEPTION-HANDLING

Here is an example of an Ada procedure where three exceptions may be raised, and how those exceptions are handled. Note that "negative_rainfall" is a user-defined and user-raised exception, and "end_error" is handled non-locally.
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
            loop
                begin
                    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");
end
 
 



Copyright (c) by Charles Elkan, 1999.