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.
employee: (# name: @text; birthday: @date; dept: ^Department; totalHours: @integer; registerWork: (# noOfHours: @integer enter noOfHours do noOfHours + totalhours -> totalHours #); computeSalary:< (# salary: @integer do inner exit salary #); #);
The elements of the employee pattern have the following meaning:
The following patterns are subpatterns of employee corresponding to salesmen and workers.
worker: employee (# seniority: @integer; computeSalary::< (# do totalHours*80+seniority*4->salary; 0->totalHours #) #); salesman: employee (# noOfSoldUnits: @integer; computeSalary::< (# do totalHours*80+noOfSoldUnits*6->salary; 0->noOfSoldUnits->totalHours #) #)
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:
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 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.
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.
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
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:
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.
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:
register: (# 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; mary->employees.insert; (if boss->employees.has then if)
The control pattern scan may be used as follows:
0->totalSalary; employees.scan (# do current.computeSalary+totalSalary->totalSalary #); totalSalary->screen.putint
This works as follows:
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.
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.
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:
productDescription: (# name: @text; price: @integer; noOfSoldUnits: @integer; order: (# orderDate: @date; c: ^customer; print:< (# do name -> puttext; 'Price: '->puttext; price -> putint; ' '->put; ' No of units sold: '->puttext; noOfSoldUnits->putint; ' '->put; orderDate.print; C.print; inner #) #) #);
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]