3 Patterns and Objects

Most object-oriented languages supporting the object-oriented perspective have constructs such as class, subclass, virtual procedure, and qualified reference variable. These constructs all originated with Simula. Eiffel and C++ include these constructs although a different terminology is used. In addition to virtual procedures BETA also has non-virtual procedures.

In this introduction, the BETA version of the above constructs will be described and compared to other object-oriented languages. The example used in the following is a company with different kinds of employees, including salesmen and workers. employee is an abstract superpattern describing the common properties of all employees.

3.1 Pattern Employee

  (# name: @text;
     birthday: @date;
     dept: ^Department;
     totalHours: @integer;
       (# noOfHours: @integer
       enter noOfHours
       do noOfHours + totalhours -> totalHours
       (# salary: @integer
       do inner
       exit salary

The elements of the employee pattern have the following meaning:

3.2 Elements of Employee

3.3 Class and procedure patterns

The following patterns are subpatterns of employee corresponding to salesmen and workers.

3.4 Subpatterns of Employee

worker: employee
  (# seniority: @integer;
       (# do totalHours*80+seniority*4->salary; 0->totalHours #)
salesman: employee
  (# noOfSoldUnits: @integer;
       (# do totalHours*80+noOfSoldUnits*6->salary; 

3.5 Part object

The above examples have shown instantiation of objects from patterns in the form of part-object attributes (like birthday: @date). An instance of, say worker, may in a similar way be generated by a declaration of the form:

mary: @worker

3.6 Dynamic reference

The above examples have also shown a dynamic reference (like dept: ^department). Such a reference is initially NONE. A dynamic reference to instances of worker may be declared as follows:

theForeman: ^worker

theForeman may be assigned a reference to the object referred by mary by execution of the following imperative:

mary[] -> theForeman[]

Note that the opposite assignment (theForeman[]->mary[]) is not legal since mary is a static reference. An instance of worker may be generated and its reference assigned to theForeman by executing the following imperative:

&worker[] -> theForeman[]

A few additional comments about constructs used so far:

In this section, it was shown how the most common object-oriented constructs may be expressed in BETA. In the following sections, examples of the more unique constructs will be given.

3.7 Singular Objects

Often there is only one object of a given type. In most languages it is necessary to make a class and generate a single instance. In BETA it is possible to describe a singular object directly. There is only one president of our company and he may be described as the following singular object:

president: @employee(# computeSalary::< (# do BIG -> salary #) #)

The declaration president is similar to the declaration of mary. The difference is that in the declaration of mary, a pattern name (worker) describes the objects whereas a complete object description is used to describe the president.

The president object is an example of a singular data object corresponding to an instance of a class pattern. It is also possible to describe singular action objects corresponding to an instance of a procedure pattern. Examples of singular action objects are given below.

3.8 Subprocedure

The previous sections has shown examples of patterns used as classes and procedures. For class patterns, examples of subpatterns have been given. Subpatterns may also be used for procedure patterns. For attributes, subpatterns may add new attributes and extend definitions of virtual patterns in the superpattern. In addition, a subpattern may specify further imperatives which have to be combined with the imperatives of the superpattern. The combination of the imperatives is handled by the inner construct. Consider the following objects:

mutex: @semaphore; sharedVar: @integer

The variable sharedVar is shared by a number of concurrent processes. Mutual access to the variable is handled by the semaphore mutex. Update of sharedVar should then be performed as follows:

mutex.P; m+sharedVar -> sharedVar; mutex.V

3.8.1 Abstract procedure pattern

This pattern of actions must be used whenever sharedVar and other shared objects have to be accessed. Instead of manipulating the semaphore directly it is possible to encapsulate these operations in an abstract procedure pattern. The pattern entry can describe this encapsulation:

entry: (# do mutex.P; inner; mutex.V #)

Execution of entry locks mutex before the inner and releases it afterwards. inner may then in subpatterns of entry be replaced by arbitrary imperatives. The subpattern updateShared of entry updates sharedVar:

updateShared: entry
  (# m: @integer 
  enter m 
  do sharedVar+m-> sharedVar

Execution of an imperative

123 -> updateShared

will then result in execution of the actions

mutex.P; sharedVar+123->sharedVar; mutex.V

We may now define an abstract superpattern corresponding to a monitor:

  (# mutex: @semaphore;
     entry: (# do mutex.P; inner; mutex.V #);
     init:< (# do mutex.V(*initially open*); inner #)

A (singular) monitor object may now be declared like shared below:

shared: @monitor
  (# var: @integer;
     update: entry(# m: @integer enter m do var+m->var #);
     get: entry(# v: @integer do var->v exit v #)

Semaphores are the basic mechanism in BETA for synchronization. They can express most synchronization problems, but may be complicated to use. It is therefore mandatory that high level abstraction mechanisms like monitor can be defined. In section 9 below, further details about concurrency in BETA will be given.

3.9 Control Patterns

Sub (procedure) patterns are used intensively in BETA for defining control patterns (control structures). This includes simple control patterns like cycle, forTo, etc. It also includes so-called iterators on data objects like list, set and register. A pattern describing a register of objects may have the following interface:

3.9.1 scan is a control pattern

  (# has: (# E: ^type; B: @boolean enter E[] do  exit B #);
     insert: (# E: ^type enter E[] do  #);
     delete: (# E: ^type enter E[] do  #);
     scan: (# current: ^type do  inner  #);

A number of details have been left out from the example. These include the representation and implementation of the register. A register may include instances of the pattern type, which has not been specified. Type is an example of a virtual class pattern which will be introduced later. For the moment type is assumed to stand for the pattern object which is a superclass of all patterns, i.e. a register may include instances of all patterns. An instance of register may be declared and used as follows:

employees: @register;

(if boss[]->employees.has then  if)

The control pattern scan may be used as follows:

3.9.2 Using a control pattern

  (# do current.computeSalary+totalSalary->totalSalary #);

This works as follows:

3.10 Nested Patterns

One of the characteristics of Algol-like languages is block-structure, which allows for arbitrary nesting of procedures. The possibility of nesting has been carried over to BETA where patterns can be arbitrarily nested. Block-structure is a powerful mechanism that extends the modeling capabilities of languages. However, besides Simula and BETA, none of the mainstream object-oriented languages supports block-structure. In most object-oriented languages, an object may be characterized by data attributes (instance variables) and procedure attributes. In BETA, an object may in addition be characterized by class pattern attributes.

3.11 BETA supports general block-structure

In the examples presented so far, there have been two levels of nesting. The outer level corresponds to class patterns, like employee, and the inner level corresponds to procedure patterns, like computeSalary. In procedural languages like Algol and Pascal it is common practice to define procedures with local procedures. This is also possible in BETA.

3.12 Nested Class Patterns

The possibility of nesting classes is a powerful feature which is not possible in languages like C++ and Eiffel. The following example shows a class pattern that describes a product of our company:

  (# name: @text;
     price: @integer;
     noOfSoldUnits: @integer;
       (# orderDate: @date;
          c: ^customer;
            do name[] -> puttext;
               'Price: '->puttext; price -> putint; ' '->put;
               ' No of units sold: '->puttext; 
               noOfSoldUnits->putint; ' '->put;

One of the attributes of a productDescription object is the class pattern order. An instance of order describes an order made on this product by some customer. The attributes of an order object include the date of the order, the number of units ordered, the customer ordering the product, and a print operation. Consider the objects:

P1,P2: @product; o1,o2: @P1.order; o3,o4: @P2.order

The objects o1 and o2 are instances of P1.order whereas o3 and o4 are instances of P2.order. The block-structure makes it possible to refer to global names in enclosing objects. In the above example, the print operation refers to names in the enclosing order object. This resembles most object-oriented languages where operations inside a procedure refer to names in the enclosing object. The print operation, however, also refers to names in the surrounding productDescription object. Execution of say o1.print will thus print the values of P1.name, P1.price, P1.noOfSoldUnits, o1.orderDate, and o1.c.

BETA Language Introduction
© 1994-2002 Mjølner Informatics
[Modified: Tuesday October 16th 2001 at 13:56]