[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Abstract classes in Oberon / OOC



At 09:59 AM 15/12/98 +0100, Michael van Acken wrote:
>Stewart Greenhill <greenhil@murdoch.edu.au> writes:
>
>> Component Pascal introduced the ABSTRACT keyword for method and class
>> declarations. If a method is tagged ABSTRACT, it is allowed to be
>> unimplemented (ie. no procedure body is specified). If a class is tagged
>> ABSTRACT, it is allowed to have abstract methods. An abstract class can
>> never be instantiated. There are a couple of subtleties that aren't
>> implemented properly, but on the whole Component Pascal gives a BIG safety
>> improvement in this respect.
>
>What are the "subtleties" that aren't implemented properly?

For the system to work properly, every exported abstract type MUST export
all of its abstract procedures. If such procedures are not implemented
within the defining module, they never will be implemented since they are
invisible from outside. Component Pascal does not enforce this. When I
raised this issue with Oberon Microsystems, they agreed that it is a
problem and will be fixed in the next version of the language specification.

Below is an example of the problem. First, I'll explain the NEW tag for
those who are not familiar with Component Pascal. NEW is a procedure
attribute that allows the compiler to distinguish between declarations that
introduce new methods and declarations that override or implement existing
methods. If you want to introduce a new method, it must be tagged NEW. This
is actually very useful because it makes clear what you intend to acheive
with a procedure declaration.

TYPE
  a = POINTER TO RECORD END;

  b = POINTER TO RECORD (a) END;

PROCEDURE (t : a) F, NEW; BEGIN  ... END F;

PROCEDURE (t : b) F, NEW; BEGIN  ... END F; (* (1) illegal *)
PROCEDURE (t : b) G, NEW; BEGIN  ... END G; (* OK *)
PROCEDURE (t : b) H; BEGIN ... END H; (* (2) illegal *)

In the above, (1) is illegal because we tried to introduce a method that
already exists. (2) is illegal because we tried to override a method that
does not exist. 

Now, here's an example of the ABSTRACT class problem:

MODULE Test1;

TYPE base * = POINTER TO ABSTRACT RECORD END;

PROCEDURE (b : base) Method(), NEW, ABSTRACT;

PROCEDURE (b : base) Handle* (), NEW;
BEGIN
  b.Method();
END Handle;

END Test1.


MODULE Test2;

IMPORT Test1;

TYPE derived = POINTER TO RECORD (Test1.base) END;

VAR d : derived;

PROCEDURE Do*;
BEGIN
  NEW(d); d.Handle()
END Do;

END Test2.

In module Test1, we defined an abstract class "base", with an abstract
method "Method". This is used by other concrete methods in Test1
("Handle"), but we forgot to export it. 

Module Test2 imports Test1, but the compiler doesn't realise that "base" is
abstract, because it has no visible abstract methods. There are two ways of
finding out what has happened here. 

Firstly, if we try to implement derived.Method, the compiler will generate
an error unless we use the NEW keyword in the declaration (introduce a new
procedure). This tells us that it does not know that "base" already has a
declaration for Method. If we had exported base.Method, we could not use
the NEW keyword because we would be implementing (overriding) the existing
declaration for Method.

Secondly, if we attempt to run Test2.Do, the system generates a Trap
because "base" has no procedure for Method in its descriptor table.

>> Would it be possible to add something like this to OOC? Here are two
>> possibilities:
>> 
>> 1) Allow [ABSTRACT] tags to be used on RECORD and PROCEDURE declarations.
>> For ABSTRACT procedures the body is ignored if specified.
>> -- OR --
>> 2) Interpret a procedure with a single HALT(abstractMethod) statement as an
>> abstract method.
>> 
>> Neither of these options requires a change to the syntax of the language.
>> They do allow the programmer to better inform to the compiler about what is
>> intended, and for the compiler to check that no unintended mistakes are
>> made. Approach (2) has the disadvantage of only allowing abstract methods
>> to be declared, not abstract classes. For the compiler to properly check
>> that you have implemented all abstract methods, it needs to know whether
>> you intend to declare an abstract or concrete class. Therefore, (1) is my
>> preferred approach.
>
>Version 2 has little appeal.  I would only support version 1 if any
>system flags "[...]" are _only_ permitted if this is explicitly
>requested in the module header.  Previously oo2c disallowed system
>flags altogether in standard modules (i.e., modules that are neiter
>INTERFACE nor FOREIGN), currently oo2c permits them anywhere so we can
>evaluate the necessary extensions for the Win32 API.

I agree. This would clearly flag it as a non-standard feature.

>> Comments? Language purists: please don't flame me too hard :-)
>
>Note that making with making it a system flag issue, this is an OOC
>specific extension.  Technically we are not touching the O2 syntax,
>but we do change O2 semantics by disallowing instanciation of certain
>user defined types.

Yes, but instantiating such types would be an error anyway, so in this
respect it better represents the intention of programmer. However we handle
abstract types, it will be non-standard. Even the HALT(abstractMethod)
convention is not portable unless there is agreement about the declaration
and interpretation of Trap codes. It is likely that these features will be
confined to a relatively small set of modules (eg. Channel and the ADT
library) since it only affects the declaration of the abstract type, not
its concrete extension.

- Stewart