Home Up Intro Contents Chapter 1 2 3 4 5 6 7 8 9 10 Design Assert Timing EBNF Report Pas Last Changed: July 12th, 1997
This is a conversion from Oberon text to HTML, and from German to English. The converter software is still under development, and some features or information may be missing in this converted version. HTML hypertext facilities are not yet active in this document. To exploit the interactive facilities, use Oberon System 3 and the source of this text, available for download using binary ftp as Oberon System 3 archive. The converter from German to English is still under development as well. A previous version is also available for Oberon V4. To access this and other additional material use ftp.
For the convenience of our students, most of this information and the related material is available in German as well.

Introduction to Oberon

The Oberon Programming Language

G. Sawitzki <gs@statlab.uni-heidelberg.de>



9 Objekts and Messages
  Oberon modules can be extended - even a long time after the development of the original module is finished, and possibly also during run time. The most impressive example is Oberon itself. For example Oberon knows the abstract type Display.Frame. A specific implementation is TextFrames.Frame which is the basis of all text frames. This implemented in Oberon as a standard model. In the course of the time variants have been added, which offer further possibilities, without modifying the original module Display. A long time after development of the standard Oberon Frames it is still possible to redefine Frames to add possibilities which had not been thought of at first - for instance Frames, which use active Internet connections or audiovisual possibilities. Try
  Desktops.OpenDoc "htt://xxxx"
where xxxx is your favorite web site to get an idea of the possibilities.

This extensibility does not need any new language constructs, but uses only by now known language characteristics - essentially the extensibility of abstract data types, which we have seen already. This is used for a particular programming style, called object-oriented programming.

Object-oriented programming can be contrasted with procedural programming. Procedural programming in the simplest case defines a program step by step. With object-oriented programming however we assume that each object has methods specifying how to react to a request. Orientation is not focussed on individual program steps, but on requests to be addressed to specific objects. A Display.Frame for instance can get the request to show itself, or to change its size or... The necessary reaction can be different depending upon the type of the display frame, and display frames of various types may be used as the application might require. We speek of polymorphism here.

The information, which is necessary to respond to a request, may vary. A text frame needs information about the text, which is to be displayed, while a network frame needs only an appropriate network reference, and an audio controller again may need quite different information. With object-oriented programming status information and references to methods how to respond to a request are encapsulated in the definition of the object.

For example the declaration of Display.Frame in the module Display is essentially based upon
  Frame*=POINTER TO FrameDesc;
  FrameDesc*=RECORD
    ....
    X*, Y*, W*, H*: INTEGER;
    handler*: PROCEDURE;
    ...
  END;

A TextFrames.Frame however has these entries, and additionally references to the text. With the possibility of extending data types, given by the Oberon language, we need not repeat the information already used with Display.Frame, but can extend these in a module TextFrames. The data types defined as extension inherit all entries of their predecessors, and can extend these by new specific fields.
  FrameDesc*=RECORD(Display.FrameDesc)
    text*: Texts.Text;
    ...
    selbeg*, selend*: Location (* current selection *)
  END;


Display.Frame has a procedure called "handler". This procedure is used to determine the reaction of variables of type Display.Frame to various requests: Various kinds of requests can be made at variables of type Display.Frame. Therefore we must parametrize the handler procedure, in order to have a possibility of indicating the type of request. An alternative would be to introduce a separate procedure for each type of request - not a very practicable solution and not extensible. The "Slot" for the procedure "handler" is inherited form Display.Frame to the type TextFrames.Frame, and TextFrames.Frame to provided ways to respond to additional types of requests. The idea to have a separate procedure for each request type is ruled out if we want extensibility. In the same way, an enumration of all request types using one of the basic types is ruled out, since it would permit no extensibility later. Finally it is to be kept in mind that requests themselves can need parameters - for example information about start and final position for a selection to be processed. Again the way out are extensible data types. The convention in Oberon is to define a type of procedure based on the following declaration:
  Handler*=PROCEDURE(frame: Frame; VAR M: Msg);
with
  Msg*=RECORD END;
and using this declaration in module Display
  Frame*=POINTER TO FrameDesc;
  FrameDesc*=RECORD
    ....
    X*, Y*, W*, H*: INTEGER;
    handler*: Handler;
    ...
  END;

The abstract type Msg does not contain any information - except the information which is implicitly contained in the type declaration. As RECORD declaration it is extensible. We can define derived types. Like the basic declaration these do not need to carry information data, as long as it is not required by our application. For example we can define:
  PrintMsg*=RECORD (Msg) END;
The only information which we need are that a PrintMsg is a well-defined request, distinct from a general abstract request of the type Msg. At execution time, when the implementation of the handler procedure is executed, it is identified using a query of the type
  IF M IS PrintMsg THEN
    (* do everything for printing*)
  ELSIF M IS ... (*andere Msg-Typen *)
  END;
If we prefer to have additional information about the number of copies to prepare, we would have used for example
  PrintMsg*=RECORD (Msg)
    copies: INTEGER
  END;


To make use of this abstract construction, we need a certain protocol. With adaptations depending on the specific type and application, we define a procedure MyHandler, say,
  PROCEDURE MyHandler*(frame: Frame; VAR M: Msg);
  BEGIN
    IF frame IS MyFrame THEN
      IF M IS ...
        ELSIF...
        ELSE...
      END;
    ELSIF ... ELSE... END;
  END MyHandler;
For example for a frame of type MyFrame, we generate a new instance using NEW
  VAR thisFrame:MyFrame;
  ...
  NEW(thisFrame)
  ...

and initialize it as
  thisFrame.handler:=MyHandler
  ... (* initialize other fields for MyFrame *)

If we identified an object obj as target of a request, specified by a message M sent to obj, we know that
  obj.handler
contains the methods necessary to hande the request. We need not inspect the object first. A call as
  obj.handler(obj, M);
leads to an execution of the specific instructions as requested.

This protocol is flexible and has all possibilities for extensions. Usually we will have one handler procedure for each object type. The protocol however is so flexible that it permits different handlers for objects of a type. We can modify the run time behaviour of a system in a flexible way.

Oberon-2 permits another simplified implementation model, however at the loss of flexibility. In place of binding the methods to individual objects, a method can be bound to object types bound in Oberon-2. Then the compiler takes over the function to initialize the method slot. To allow this, an addition target parameter is used. We have already seen this target parameter in the syntax of a procedure declaration.

Procedure Syntax:
  PROCEDURE [*] [target] procedure name [formal parameter list];
    declaration sequence
  [BEGIN
    statement sequence  ]
  END
procedure name;
The syntax for the target parameter is
  ( [VAR] name: type )
If a target is indicated, this signals that the target type needs a method. The method field is not listed in the type declaration, but added automatically by the compiler. Each variable of the target type is supplied automatically with the appropriate method, as implemented by the procedure. The procedure is now bound to the type, not to individual variables of this type. Within the procedure body the target parameter can be used like every other parameter.

With type bound procedures it is possible to use information about the type hierarchy. This can be used through "up calls": if x is a variable of the type T2 with a method handler, and T2 is an extension of type T1, the x.handler calls the appropriate method of T2, and x.handler^ calls the corresponding method of its predecessor, i.e. T1. The compiler keeps track of the type hierarchy and can find this method. A usual construction is
  PROCEDURE (obj: T2) handler(obj:T1;VAR M:Msg);
  BEGIN
    IF M IS ..(* new message types of the extension T2 *)
    THEN
    ELSE obj.handler^(obj,M)(* up call to handle messages
       which are already known to T1 *)
    END
  END handler;


In contrast to the first method, with type bound procedures all object of one type share the same procedures. It is not possible to adjust them for specific objects.

Literature: J.L. Marais: Extensible Software Systems in Oberon. Journal of Computational and Graphical Statistics, 5.3 (1996) 284-298



Introduction to the Oberon programming language. ItO/Ch09.Text
gs (c) G. Sawitzki, StatLab Heidelberg
<http://statlab.uni-heidelberg.de/projects/oberon/intro/>



Home Up Intro Contents Chapter 1 2 3 4 5 6 7 8 9 10 Design Assert Timing EBNF Report Pas