MIA 94-26: BETA Language Introduction MIA 94-26: BETA Language Introduction Table of Contents Copyright Notice................................................................................................................................1 Introduction........................................................................................................................................3 Language Concepts..........................................................................................................................4 Powerful Abstraction Mechanisms..................................................................................................5 The pattern.........................................................................................................................................6 Subpattern..........................................................................................................................................7 Virtual pattern....................................................................................................................................8 Pattern variable..................................................................................................................................9 Coroutines and concurrency..........................................................................................................10 Identification of Objects..................................................................................................................11 Class-less objects...........................................................................................................................12 Classification...................................................................................................................................13 Composition (Aggregation)............................................................................................................14 Inheritance........................................................................................................................................15 Conceptual Framework...................................................................................................................16 Modeling...........................................................................................................................................17 Basic Constructs.............................................................................................................................18 Simple Types and Values................................................................................................................19 Simple Static Variables...................................................................................................................20 Simple Assignments.......................................................................................................................21 Value assignment............................................................................................................................22 Control Structures...........................................................................................................................23 if.........................................................................................................................................................24 for......................................................................................................................................................25 descriptor.........................................................................................................................................26 labeled descriptor............................................................................................................................27 i MIA 94-26: BETA Language Introduction Table of Contents labeled imperative...........................................................................................................................28 Static and Dynamic Variables.........................................................................................................29 Reference attributes........................................................................................................................30 Static Reference...............................................................................................................................31 Dynamic reference...........................................................................................................................32 Reference assignment....................................................................................................................33 Repetitions.......................................................................................................................................34 Composite Types (Records)...........................................................................................................35 Pattern..............................................................................................................................................36 Patterns and Objects.......................................................................................................................37 Pattern Employee............................................................................................................................38 Elements of Employee....................................................................................................................39 Class and procedure patterns........................................................................................................40 Subpatterns of Employee...............................................................................................................41 Part object........................................................................................................................................42 Dynamic reference...........................................................................................................................43 Singular Objects..............................................................................................................................44 Subprocedure..................................................................................................................................45 Abstract procedure pattern............................................................................................................46 Control Patterns...............................................................................................................................47 scan is a control pattern.................................................................................................................48 Using a control pattern...................................................................................................................49 Nested Patterns...............................................................................................................................50 BETA supports general block-structure........................................................................................51 Nested Class Patterns.....................................................................................................................52 Virtual Pattern..................................................................................................................................53 ii MIA 94-26: BETA Language Introduction Table of Contents Structural equivalence is used in BETA.......................................................................................54 Patterns A and AA...........................................................................................................................55 Combined descriptor.......................................................................................................................56 Pattern AB........................................................................................................................................57 Pattern AAA......................................................................................................................................58 Final binding....................................................................................................................................59 Virtual Procedure Pattern...............................................................................................................60 ComputeSalary is a virtual procedure pattern..............................................................................61 Virtual Class Pattern........................................................................................................................62 type is a virtual class pattern.........................................................................................................63 General parameterized patterns ....................................................................................................64 Coroutines and Concurrency.........................................................................................................65 Components with execution threads.............................................................................................66 Coroutines........................................................................................................................................67 Suspending and resuming..............................................................................................................68 Concurrency.....................................................................................................................................69 Monitor Example..............................................................................................................................70 Procedure pattern conc..................................................................................................................71 Rendezvous Example......................................................................................................................72 More information.............................................................................................................................74 Inheritance........................................................................................................................................75 Classification and inheritance .......................................................................................................76 Inheritance from part-objects.........................................................................................................77 iii Copyright Notice Mjølner Informatics Report MIA 94-26 August 1999 Copyright © 1994-99 Mjølner Informatics. All rights reserved. No part of this document may be copied or distributed without the prior written permission of Mjølner Informatics Copyright Notice 1 MIA 94-26: BETA Language Introduction Beta Language Introduction Copyright Notice 2 Introduction This report is a an introduction to the BETA language. The BETA language is presented to someone who is familiar with one or more object-oriented language such as C++ or Eiffel. The overall aspects of the BETA language is presented. The presentation focuses on the concepts and ideas behind the design of BETA, and includes examples on the use of most constructs. The tutorial contains sections on basic constructs, patterns and objects, singular objects, subprocedure, control patterns, nested patterns, virtual patterns, coroutines, concurrency, and inheritance. For more details about the BETA language than presented in this tutorial please see [MMN 93]. For a tutorial on the Mjølner System, please see[MIA 94-24] This introduction is based on a chapter in the book [Knudsen 94] written by Ole Lehrmann Madsen. Beta Language Introduction Mjølner Informatics Beta Language Introduction Introduction 3 Language Concepts BETA is a modern object-oriented language from the Scandinavian school of object-orientation where the first object-oriented language Simula was developed. BETA supports the object-oriented perspective on programming and contains comprehensive facilities for procedural and functional programming. BETA has powerful abstraction mechanisms for supporting identification of objects, classification and composition. BETA is a strongly typed language like Simula, Eiffel and C++ with most type checking being carried out at compile-time. It is well known that it is not possible to obtain all type checking at compile time without sacrificing the expressiveness of the language. BETA has an optimum balance between compile-time type checking and run-time type checking. Beta Language Introduction Mjølner Informatics Language Concepts Language Concepts 4 Powerful Abstraction Mechanisms BETA has powerful abstraction mechanisms that provide excellent support for design and implementation, including data definition for persistent data. The powerful abstraction mechanisms greatly enhance reusability of designs and implementations. Powerful Abstraction Mechanisms 5 The pattern The abstraction mechanisms include class, procedure, function, coroutine, process, exception and many more, all unified into the ultimate abstraction mechanism: the pattern. In addition to the pattern, BETA has subpattern, virtual pattern and pattern variable. This unification gives a uniform treatment of abstraction mechanisms and a number of new ones. Most object-oriented languages have classes, subclasses and virtual procedures, and some have procedure variables. Since a pattern is a generalization of abstraction mechanisms like class, procedure, function, etc., the notions of subpattern, virtual pattern and pattern variable also apply to these abstraction mechanisms. In addition to the above mentioned abstraction mechanisms, the pattern subsumes notions such as generic package and task type as known from Ada. The pattern 6 Subpattern The subpattern covers subclasses as in most other object-oriented languages. In addition, procedures may be organized in a subprocedure hierarchy in the same way as classes may be organized in a subclass hierarchy. Since patterns may also be used to describe functions, coroutines, concurrent processes, and exceptions, these may also be organized in a pattern hierarchy. Subpattern 7 Virtual pattern The notion of virtual pattern covers virtual procedures as in C++. In addition, virtual patterns cover virtual classes, virtual coroutines, virtual concurrent processes, and virtual exceptions. Virtual classes provide a more general alternative to generic classes as in Eiffel or templates as in C++. Virtual pattern 8 Pattern variable BETA includes the notion of pattern variable. This implies that patterns are first class values, that may be passed around as parameters to other patterns. By using pattern variables instead of virtual patterns, it is possible dynamically to change the behavior of an object after its generation. Pattern variables cover procedure variables (i.e. a variable that may be assigned different procedures). Since patterns may be used as classes, it is also possible to have variables that can be assigned classes, etc. Beta Language Introduction Mjølner Informatics Language Concepts Pattern variable 9 Coroutines and concurrency BETA does not only allow for passive objects as in C++ and Eiffel. BETA objects may also act as coroutines, making it possible to model alternating sequential processes and quasi-parallel processes. BETA coroutines may be executed concurrent (non pre-emptive scheduling in current implementation). The basic mechanism for synchronization is semaphores, but high-level abstractions for synchronization and communication, hiding all details about semaphores, are easy to implement, and the standard library includes monitors, and rendezvous. The user may easily define new concurrency abstractions including schedulers for processes. BETA supports the three main subfunctions of abstraction: identification, classification, and composition as described in the following. Beta Language Introduction Mjølner Informatics Language Concepts Coroutines and concurrency 10 Identification of Objects Identification of Objects 11 Class-less objects It is possible to describe objects that are not generated as instances of a class pattern, so-called òclass-less objectsó. This is in many cases useful when there is only one object of a kind. In most object-oriented languages, it is necessary to define superfluous classes for such objects. In analysis and design, it is absolutely necessary to be able to describe singular objects without having them as instances of classes. Beta Language Introduction Mjølner Informatics Language Concepts Class-less objects 12 Classification Classification is supported by patterns, subpatterns, and virtual patterns that make it possible to describe classification hierarchies of objects and patterns (objects, classes, procedures, functions, coroutines, processes, exceptions, etc.). Beta Language Introduction Mjølner Informatics Language Concepts Classification 13 Composition (Aggregation) Objects and patterns may be defined as a composition of other objects and patterns. The support for composition includes: • Whole-part composition: an attribute of an object may be a part-object. This makes it possible to describe objects in terms of their physical parts. • Reference composition: an attribute may be a reference to an object. Reference composition is the basis for modeling arbitrary relations between objects. • Localization: an attribute of an object may be a (nested) patternÑalso known as block-structure. The block-structure makes it easy to create arbitrary nested patterns. This makes it possible for objects to have local patterns used as classes, procedures, etc. Local patterns greatly enhance the modeling capabilities of an object-oriented language. Beta Language Introduction Mjølner Informatics Language Concepts Composition (Aggregation) 14 Inheritance In BETA, inheritance is not only restricted to inheritance from superpatterns. It is also possible to inherit from a part-object. Virtual patterns in the part-object may be redefined to influence the enclosing object. Multiple inheritance is supported through inheritance from multiple part-objects. This gives a much cleaner structure than inheritance from multiple superpatterns. Beta Language Introduction Mjølner Informatics Language Concepts Inheritance 15 Conceptual Framework BETA is intended for modeling and design as well as implementation. During the design of BETA the development of the underlying conceptual framework has been just as important as the language itself. Conceptual Framework 16 Modeling BETA is a language for representing/modeling concepts and phenomena from the application domain and for implementing such concepts and phenomena on a computer system. Part of a BETA program describes objects and patterns that represent phenomena and concepts from the application model. This part is said to be representative since BETA elements at this level are meaningful with respect to the application domain. Other parts of a BETA program are non-representative, since they do not correspond to elements of the application domain, but are intended for realizing the model as a computer system. The BETA language as presented in this introduction is for describing objects and patterns. The objects and patterns constitute the logical structure of a program execution. The physical structure of a program execution is handled by other components of the Mjølner System. A tutorial on using the this system is given in [MIA 94-24]. Beta Language Introduction Mjølner Informatics Beta Language Introduction Modeling 17 Basic Constructs The most fundamental elements of BETA are objects and patterns. This section describes the basic patterns and values, simple assignments, control structures, variable declarations, repetitions and patterns used as composite types. Beta Language Introduction Mjølner Informatics Basic Constructs Basic Constructs 18 Simple Types and Values The simple types (or also called basic patterns) are integer, boolean, char, and, real. The following table shows the simple types with examples of values, including text constant. Notice, that text is not a simple type in BETA, but a pattern defined in the basic BETA environment called betaenv. Type Value integer7, -4, 0x4FFC, 2x101101 booleantrue, false char'c' real3.141, -1.234E3 text constant'abc' Beta Language Introduction Mjølner Informatics Basic Constructs Simple Types and Values 19 Simple Static Variables In BETA, a static variable (also called a static reference) is declared like: i @integer; r: @real; Variables of the simple types can only be declared static, see below for dynamic references. Beta Language Introduction Mjølner Informatics Basic Constructs Simple Static Variables 20 Simple Assignments Simple Assignments 21 Value assignment Simple value assignments in BETA goes left to right: 2 -> i i -> j i*j -> k (i,j) -> (x,y) (* assign (* assign (* assign (* assign * *) the the the the the Beta Language Introduction value value value value value 2 to i *) of i to j *) of i*j to k *) of i to x and of j to y Mjølner Informatics Basic Constructs Value assignment 22 Control Structures BETA has two build-in control structures: if and for, both having two forms. The simple if imperative with one boolean expression: Control Structures 23 if (if <expression> then <imperatives> else <imperatives> if) and the if with several alternatives: (if <expression> // <expression> then <imperatives> // <expression> then <imperatives> else <imperatives> if) where // means equals. The simple for imperative just iterates a given number of times: if 24 for (for <expression> repeat <imperatives> for) but the for imperative may implicitly declare an iteration variable, only available inside the for loop, by: (for <variable>: <expression> repeat <imperatives> for) The for loop always starts in 1 and stops at <expression>. The loop can be terminated or restarted using labels, see below. The following BETA code is a general object-descriptor (or descriptor for short): for 25 descriptor <declarations> enter <enter-list> do <imperatives> exit <exit-list> A descriptor consists of type and variable declarations, an enter part for parameters (enter <enter-list>), a do-part for the action (do <imperatives>), and finally an exit part for the results (exit <exit-list>). All elements are optional. A descriptor can be labeled, and the descriptor can be restarted and/or left using the label: descriptor 26 labeled descriptor L: (# leave L restart L #) In general any imperative can have a label: labeled descriptor 27 labeled imperative L: <imperative> L: (if leave L L: (for leave L if) for) leave L implies that control is transferred to immediately after the labeled imperative/descriptor. restart L implies that control is transferred to immediately before the labeled imperative/descriptor. Beta Language Introduction Mjølner Informatics Basic Constructs labeled imperative 28 Static and Dynamic Variables Static and Dynamic Variables 29 Reference attributes In BETA variables are two examples of reference attributesÑstatic references that constantly denote the same object, and dynamic references that may denote different objects. Reference attributes 30 Static Reference Examples of static reference variables are: i: @integer (* i refers to a simple type: integer *) p: @A (* an instance of A is automatically generated and * p always refers to this object *) s: @(# #) (* an instance of (# #) is automatically generated * and s always refers to this singular object *) Static Reference 31 Dynamic reference Examples of dynamic reference variables are: i: ^integerObject p: ^A Assignments between dynamic references can be done using the reference operator '[]' (read box): Dynamic reference 32 Reference assignment p1[] -> p2[] (* reference assignment *) Dynamic reference variables are initially NONE i.e. refers to nothing. Objects can be created using the new operator '&': &A[] -> p[] (* create an instance of A and assign the reference * to p *) It is illegal to declare dynamic references to simple types: i: ^integer (* ILLEGAL *) r: ^real (* ILLEGAL *) Instead use integerObject, charObject, booleanObject, or realObject defined in the Mjølner System basic betaenv environment. Beta Language Introduction Mjølner Informatics Basic Constructs Reference assignment 33 Repetitions In BETA it is possible to declare a repetition of static (simple types) or dynamic references. A repetition is declared like: R: [10] @integer P: [10] ^A R[1] -> i P[1][] -> x[] RR: [1] @integer R -> RR R.range n -> R.extend n -> R.new (* repetition of 10 static references *) (* repetition of 10 dynamic references *) (* value assignment *) (* reference assignment *) (* repetition of 1 static reference *) (* repetition assignment: * all values from R is copied into RR * RR is automatically extended if needed *) (* the size of the repetition *) (* extends the repetition with n elements *) (* allocates a new repetition with n elements *) The range of a repetition is 1 to R.range, thus repetitions always start with 1. Beta Language Introduction Mjølner Informatics Basic Constructs Repetitions 34 Composite Types (Records) Using the object-descriptor it is possible to declare composite types: point: (# x,y: @integer #) (* point is a composite type * consisting of two integers *) p: @point (* static declaration of a point *) p.x (* remote access to x *) circle: (* composite type using simple and composite types *) (# center: @point; radius: @integer; #) Composite Types (Records) 35 Pattern The declaration of point and circle above is in general called a pattern declaration. The pattern will be described in details in the following sections. Beta Language Introduction Mjølner Informatics Beta Language Introduction Pattern 36 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. Patterns and Objects 37 Pattern Employee 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: Pattern Employee 38 Elements of Employee • The attributes name, birthday, dept and totalHours are reference attributes denoting instances of the patterns text, date, department and integer respectively. • Name, birthday, and totalHours refer to part-objects. A part-object is a fixed part of its enclosed object and is generated together with the enclosing object. Part-objects are also found in Eiffel and C++. • Dept is a dynamic reference that either has the value NONE or refers to a separate instance of the pattern department. • The attributes registerWork, and computeSalary are pattern attributes describing actions to be executed. They correspond to procedures in most other languages. The enter-part describes the input parameters of a pattern and the exit-part describes its output parameters. registerWork has one input parameter noOfHours and computeSalary has one output parameter, salary. • registerWork is a non-virtual pattern attribute. This means that its complete description is given as part of the description of employee. It is similar to non-virtual functions in C++. • computeSalary is a virtual pattern attribute (specified by using the '' symbol). Only part of its description is given since the computation of the salary is different for salesmen and workers. The description of a virtual pattern may be extended in subpatterns of employee. A virtual pattern attribute is similar to a virtual function in C++. Elements of Employee 39 Class and procedure patterns • employee, registerWork and computeSalary are all examples of patterns. employee is an example of a pattern used as a class and is therefore called a class pattern. registerWork and computeSalary are examples of patterns used as procedures and are therefore called procedure patterns. Technically there is no difference between class patterns and procedure patterns. The following patterns are subpatterns of employee corresponding to salesmen and workers. Class and procedure patterns 40 Subpatterns of Employee worker: employee (# seniority: @integer; computeSalary::< (# do noOfHours*80+seniority*4->salary; 0->totalHours #) #); salesman: employee (# noOfSoldUnits: @integer; computeSalary::< (# do noOfHours*80+noOfSoldUnits*6->salary; 0->noOfSoldUnits->totalHours #) #) • The class pattern worker adds the attribute seniority and extends the definition of computeSalary. The salary for a worker is a function of the noOfHours being worked and the seniority of the worker. • The class pattern salesman adds the attribute noOfSoldUnits and describes another extension of computeSalary. The salary for a salesman is a function of the noOfHours being worked and the noOfSoldUnits. • The symbol '' describe the fact that the definition of computeSalary from the superpattern employee is extended. Subpatterns of Employee 41 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 Part object 42 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: • The symbol & means new. • The symbol -> is used for assignment of state. • An expression R[] denotes the reference to the object referred by R, whereas an expression R denotes the object itself. The above assignment thus means that the qualified reference theForeman is assigned a reference to the generated instance of worker. • An assignment of the form mary->theForeman means that the state of the object referred by mary is enforced upon the state of the object referred by theForeman. This form of assignment is called value assignment. If X and Y are integer objects then X -> Y means that the value of X is assigned to the object Y. 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. Beta Language Introduction Mjølner Informatics Beta Language Introduction Dynamic reference 43 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. Beta Language Introduction Mjølner Informatics Beta Language Introduction Singular Objects 44 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 Subprocedure 45 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: 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. Beta Language Introduction Mjølner Informatics Beta Language Introduction Abstract procedure pattern 46 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: Control Patterns 47 scan is a control pattern register: (# has: (# E: ^type; B: @boolean enter E[] do insert: (# E: ^type enter E[] do #); delete: (# E: ^type enter E[] do #); scan: (# current: ^type do inner #); exit B #); #) 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: scan is a control pattern 48 Using a control pattern 0->totalSalary; employees.scan (# do current.computeSalary+totalSalary->totalSalary #); totalSalary->screen.putint This works as follows: • The imperative employees.scan(# #) is an example of a singular action object as mentioned in section 4. • The do-part of scan has an inner imperative which is executed for each element in the register. The details of this are not shown, but it may be implemented as a loop that steps through the elements of the register and executes inner for each element. • The attribute current of scan is used as an index variable that for each iteration refers to the current element of the register. This may be implemented by assigning the reference of the current element to current before inner is executed. • The effect of executing the above singular action object is that current.computeSalary+totalSalary->totalSalary is executed for each element in the register. Beta Language Introduction Mjølner Informatics Beta Language Introduction Using a control pattern 49 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. Nested Patterns 50 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. BETA supports general block-structure 51 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: 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 Mjølner Informatics Beta Language Introduction Nested Class Patterns 52 Virtual Pattern Virtual Pattern 53 Structural equivalence is used in BETA In the example in section 3 it was mentioned that a redefinition of a virtual procedure pattern is not a redefinition (overriding) as in C++. In fact a virtual pattern in BETA can only be extended and cannot be completely redefined. The rationale behind this is that a subpattern should have the same properties as its superpattern including which imperatives are executed. Ideally a subpattern should be behaviorally equivalent to its superpattern. This will, however, require a correctness proof. The subpattern mechanism of BETA supports a form of structural equivalence between a subpattern and its superpattern. Consider the following patterns: Structural equivalence is used in BETA 54 Patterns A and AA A: (# V:< (# x: do I1; inner; I2 #) #); AA: A(# V::< (# y: do I3; inner; I4#) #) The pattern A has a virtual procedure attribute V. V has an attribute x and its do-part contains the execution of I1; inner; I2. The subpattern AA of A extends the definition of V. The extended definition of V in AA corresponds to the following object-descriptor (except for scope rules): Patterns A and AA 55 Combined descriptor (# x: ; y: do I1; I3; inner; I4; I2 #) As may be seen the V attribute of AA has the attributes x and y and the do-part consists of I1; I3; inner; I4; I2. The definition of V is an extension of the one from A and not a replacement. The subpattern AB of A describes another extension of V: Combined descriptor 56 Pattern AB AB: A(# V::< (# z: do I5; inner; I6 #) #) Here V corresponds to the following object descriptor: V: (# x: ; z: do I1; I5; inner; I6; I2 #) The definition of V may be further extended in subpatterns of AA also as shown in the definition AAA: Pattern AB 57 Pattern AAA AAA: AA(# V::< (# q: do I7; inner; I8 #) #) The definition of V corresponds to the following object-descriptor: V: (# x: ; y: ; q: do I1; I3; I7; inner; I8; I4; I2 #) As may be seen, the pattern V is a combination of the definitions of V from A, AA and AAA. Pattern AAA 58 Final binding The definition of V may be extended using a final binding () in subpatterns of A as shown in the definition AC: AC: A(# V::(# q: do I2; inner; I4 #) #) The final binding of V means that V cannot be extended in subpatterns of AC. The extended definition of V in AC corresponds to the following object-descriptor (except for scope rules): (# x: ; y: do I1; I3; inner; I4; I2 #) The virtual mechanism in BETA guarantees that behavior defined in a superpattern cannot be replaced in a subpattern. This form of structural equivalence is useful when defining libraries of patterns that are supposed to execute a certain sequence of actions. In C++, the programmer must explicitly invoke the actions from the superclass by means of superclass::functionname. This is illustrated by the example in the next section. The inner construct is more general than shown above, since a pattern may have more than one inner and inner may appear inside control structures and nested singular object descriptors. Beta Language Introduction Mjølner Informatics Virtual Pattern Final binding 59 Virtual Procedure Pattern The attribute computeSalary of pattern employee is an example of a virtual procedure pattern. In this example the do-part of the virtual definition in employee is very simple, only consisting of an inner-imperative. The extended definitions of computeSalary in worker and salesman both include the code noOfHours*80 and 0->totalHours. This code may instead be defined in the definition of computeSalary in employee as shown below: Virtual Procedure Pattern 60 ComputeSalary is a virtual procedure pattern employee: (# computeSalary:< (# salary: @integer do noOfHours*80->salary; inner; 0->totalHours exit salary #) #); worker: employee (# computeSalary::< (# do seniority*4+salary->salary; inner #) #); salesman: employee (# computeSalary::< (# do noOfSoldUnits*6+salary ->salary; 0 ->noOfSoldUnits; inner #) #) The extended definitions of computeSalary in worker and salesman have an inner to enable further extensions of computeSalary in subpatterns of worker and salesman. Beta Language Introduction Mjølner Informatics Virtual Pattern ComputeSalary is a virtual procedure pattern 61 Virtual Class Pattern Virtual patterns may also be used to parameterize general container patterns such as the register pattern described above. For the register pattern we assumed the existence of a type pattern defining the elements of the register, i.e. elements of a register must be instances of the pattern type. The pattern type may be declared as a virtual pattern attribute of register as shown below: Virtual Class Pattern 62 type is a virtual class pattern register: (# type:< object; insert:< (# e: ^type enter e[] do #) #) The declaration type:< object specifies that type is either the pattern object or some subpattern of object. In the definition of register, type may be used as an alias for object, e.g. references qualified by type are known to be at least Objects. Since object is the most general superpattern, type may potentially be any other pattern. The virtual attribute type may be bound to a subpattern of object in subpatterns of register. The following declaration shows a pattern workerRegister which is a register where the type attribute has been bound to worker: workerRegister: register (# type::< worker; findOldestSeniority: (# old: @integer do scan (# do (if current.seniority > old then current.seniority->old if)#) exit old #) #); In the definition of workerRegister, the virtual pattern type may be used as a synonym for the pattern worker. This means that all references qualified by type may be used as if they were qualified by worker. The reference current of the scan operation is used in this way by the operation findOldestSeniority which computes the oldest seniority of the register. The expression current.seniority is legal since current is qualified by type which in workerRegister is at least a worker. In subpatterns of workerRegister it is possible to make further bindings of type thereby restricting the possible members of the register. Suppose that manager is a subpattern of worker. A manager register may then be defined as a subpattern of workerRegister: managerRegister: workerRegister(# type::< manager #) In the definition of managerRegister, type may be used as a synonym for manager, i.e. all references qualified by type are also qualified by manager. type is a virtual class pattern 63 General parameterized patterns Virtual patterns make it possible to define general parameterized patterns like register and to restrict the member type of the elements. In this way virtual class patterns provide an alternative to templates as found in C++. Beta Language Introduction Mjølner Informatics Beta Language Introduction General parameterized patterns 64 Coroutines and Concurrency A BETA object may be the basis for an execution thread. Such a thread will consist of a stack of objects currently being executed. An object which can be used as the basis for an execution thread has to be declared as an object of kind component as shown in the following declaration: Coroutines and Concurrency 65 Components with execution threads A: @| activity The symbol ò|ó describes that the object A is a component. A component (thread) may be executed as a coroutine or it may be forked as a concurrent process. Consider the following description of activity: activity: (# do cycle (# do getOrder; suspend; processOrder; suspend; deliverOrder; suspend #)#) The component object may be invoked by an imperative A which implies that the do-part is executed. The execution of A is temporarily suspended when A executes a suspend-imperative. In the above example this happens after the execution of getOrder. A subsequent invocation of A will resume execution after the suspend-imperative. In the above example this means that processOrder will be executed. If B is also an instance of activity, then the calling object may alternate between executing A and B: cycle(# do A; B; #) The above example shows how to use components as deterministic coroutines in the sense that the calling object controls the scheduling of the coroutines. In section 9.1 below another example of using coroutines will be given. It is also possible to execute component objects concurrently. By executing A[]->fork; B[]->fork the component objects A and B will be executed concurrently. As for the deterministic coroutine situation, A and B will temporarily suspend execution when they execute a suspend-imperative. Further examples of concurrent objects will be given below in section 9.2. Beta Language Introduction Mjølner Informatics Coroutines and Concurrency Components with execution threads 66 Coroutines Deterministic coroutines have demonstrated their usefulness through many years of usage. Below we give a typical example of using coroutines. Suppose we have a register for the permanent workers and another one for the hourly paid workers. Suppose also that it is possible to sort these registers according to a given criterion like the total hours worked by the employee. Suppose that we want to produce a list of names of all employees sorted according to the total hours worked. This may be done by merging the two registers. A register object has a scan operation that makes it possible to go through all elements of the register. Instead we define an operation of register in the form of a coroutine getNext, which delivers the next element of the register when called: register: (# getNext: | @ (# elm: ^employee do scan(# do current[]->elm[]; suspend #); none->elm[] exit elm[] #); #); pReg: @permanentRegister; hReg: @hourlyPaidRegister; pReg.getNext->e1[]; hReg.getnext->e2[]; L: cycle (# do (if e1[] = none then (*empty hReg*); leave L if); (if e2[] = none then (*empty pReg*); leave L if); (if e1.totalHours < e2.totalHours then e1.print; pReg.getNext->e1[] else e2.print; hReg.getNext->e2[] if) #) Coroutines 67 Suspending and resuming The attributes getNext of the objects pReg and hReg have their own thread of execution. When called in an imperative like pReg.getNext->e1[], the thread is executed until it either executes a suspend or terminates. If it executes a suspend, it may be called again in which case it will resume execution at the point of suspend. The first time getNext is called, it will start executing scan. For each element in the register, it will suspend execution and exit the current element via the exit variable elm[]. When the register is empty, NONE is returned. Beta Language Introduction Mjølner Informatics Coroutines and Concurrency Suspending and resuming 68 Concurrency As previously mentioned, it is possible to perform concurrent execution of components by means of the fork operation as sketched in the following example: (# S1: @| S2: @| S3: @| do S1[] #) (# do #); (# do #); (# do #) -> fork; S2[] -> fork; S3[] -> fork; The execution of S1, S2 and S3 will take place concurrently with each other and with the object executing the fork operations. Concurrent objects may access the same shared objects without synchronization, but may synchronize access to shared objects by means of semaphores. In section 5 above the pattern semaphore has been described. It is well known that a semaphore is a low level synchronization mechanism which may be difficult to use in other than simple situations. For this reason the Mjølner library has a number of patterns defining higher level synchronization mechanisms. This library includes a monitor pattern as described in section 5 above. The library also includes patterns defining synchronization in the form of rendezvous as in Ada. Concurrency 69 Monitor Example The following example describes a company with a number of salesmen, workers and carriers. The salesmen obtain orders from customers and store them in an order pool. The workers obtain orders from the order pool, process them and deliver the resulting item in an item pool. The carriers pick up the items from the item pool and bring them to the customer. Salesmen, workers and carriers are described as active objects whereas the order- and item pools are represented as monitor objects. (# salesman: employee (# getOrder: (# exit anOrder[] #) do cycle (# do getOrder -> jobPool.put #) #); S1,S2, : @|salesman; jobPool: @monitor (# jobs: @register(# type::< order #); put: entry (# ord: ^order enter ord[] do ord[] ->jobs.insert #); get: entry (# ord: ^order do jobs.remove -> ord[] exit ord[] #) #); worker: employee (# processJob: (# enter anOrder[] do exit anItem[] #) do cycle(# do jobPool.get -> processJob -> itemPool.put #) #); W1,W2,: @| worker; itemPool: @monitor(# #); carrier: employee (# deliverItem: (# enter anItem[] do #) do cycle(# do itemPool.get ->DeliverItem #) #); C1,C2, : @| carrier; do jobPool.init; itemPool.init; conc(# do S1[]->start; W1[]->start; C1[]->start; #) #) Monitor Example 70 Procedure pattern conc The procedure pattern conc is another example of a high-level concurrency pattern from the Mjølner library. It does not terminate execution until components being started (by S1[]->start, etc.) have terminated their execution. Procedure pattern conc 71 Rendezvous Example Next we show an example of using the library patterns for describing synchronized rendezvous. The example shows a drink machine that provides coffee and soup. A customer operates the machine by pushing either makeCoffee or makeSoup. If makeCoffee has been pushed, then the customer may obtain the coffee by means of getCoffee. Similarly if makeSoup has been pushed then the soup may be obtained by means of getSoup. The system pattern has a port attribute which may be used to define synchronization ports. The drink machine described below has three such ports, activate, coffeeReady, and soupReady. A port object has a pattern attribute entry which may be used to define procedure patterns associated with port. For the port activate, two procedure patterns makeCoffee and makeSoup are defined. For coffeeReady and soupReady, the procedure patterns getCoffee and getSoup are defined. An execution of a port-entry operation like aDrinkMachine.makeCoffee will only be executed if the drinkMachine has executed a corresponding accept by means of activate.accept. • Initially a drinkMachine is ready to accept either makeCoffee or makeSoup. • If e.g. makeCoffee is executed, then when òthe coffee has been madeó, the drinkMachine is willing to accept the operation getCoffee. This is signaled by executing an accept on the port coffeeReady. Technically this is implemented by assigning a reference to coffeeReady to the port reference drinkReady. The do-part of drinkMachine then makes an accept on drinkReady. • When the operation getCoffee, has been executed, the drinkMachine is again ready to accept a new operation associated with the activate port. drinkMachine: system (# activate: @port; makeCoffee: activate.entry (# do coffeeReady[]->drinkReady[] #); makeSoup: activate.entry(# do soupReady[]->drinkReady[] #); coffeeReady, soupReady: @port; getCoffee: coffeeReady.entry(# do exit someCoffee [] #); getSoup: soupReady.entry(# do exit someSoup [] #); drinkReady: ^port do cycle(# do activate.accept; drinkReady.accept #) #) The drinkMachine may be used in the following way: aDrinkMachine: @| drinkMachine aDrinkMachine.makeCoffee; aDrinkMachine.getCoffee; aDrinkMachine.makeSoup; aDrinkMachine.getSoup; As may be seen the use of the patterns system, port and entry makes it possible to describe a concurrent program in the style of Ada tasks that synchronize their execution by means of rendezvous. A port object defines two semaphores for controlling the execution of the associated entry patterns. The actual details will not be given in this language introduction. It is possible to specialize the drinkMachine into a machine that accepts further operations: extendedDrinkMachine: drinkMachine (# makeTea: activate.entry(# do teaReady[]->drinkReady[] #); teaReady: @port; getTea: teaReady.entry(# exit someTea[] #) #) Rendezvous Example 72 MIA 94-26: BETA Language Introduction The extendedDrinkMachine inherits the operations and protocol from drinkMachine and adds new operations to the protocol. The basic mechanisms in BETA for providing concurrency are component-objects (providing threads), the fork-imperative (for initiating concurrent execution) and the semaphore (for providing synchronization). As has been mentioned already, these mechanisms are inadequate for many situations. The abstraction mechanisms of BETA make it possible to define higher-level abstractions for concurrency and synchronization. Rendezvous Example 73 More information Please see the manual [MIA 90-8] for details about the concurrency library. Beta Language Introduction Mjølner Informatics Beta Language Introduction More information 74 Inheritance The subpattern mechanism combined with the possibility of redefining/extending virtual procedures is widely recognized as a major benefit of object-oriented languages. This mechanism is often called inheritance since a subpattern is said to inherit properties (code) from its superpattern. Inheritance makes it easy to define new patterns from other patterns. In practice this has implied that subpatterns are often used for sheer inheritance of code without any concern for the relation between a pattern and its subpatterns in terms of generalization/specialization. The use of multiple inheritance is in most cases justified in inheritance of code and may lead to complicated inheritance structures. Beta Language Introduction Mjølner Informatics Inheritance Inheritance 75 Classification and inheritance In BETA subpatterns are intended for representing classification and inheritance of code is a (useful) side effect. In BETA it is not possible to define a pattern with multiple superpatterns corresponding to multiple inheritance. There are indeed cases where it is useful to represent classification hierarchies that are not tree structured. However, a technical solution that justifies the extra complexity has not yet been found. Beta Language Introduction Mjølner Informatics Inheritance Classification and inheritance 76 Inheritance from part-objects BETA does support multiple inheritance, but in the form of inheritance from part-objects. A compound object inherits from its parts as well as its superpattern. The reason that this has not been more widely explored/accepted is that in most languages inheritance from part-objects lacks the possibility of redefining/extending virtual procedures in the same way as for inheritance from superpatterns. Block-structure and singular objects make this possible in BETA. Assume that we have a set of patterns for handling addresses. An address has properties such as street name, street number, city, etc., and a virtual procedure for printing the address. In addition we have a pattern defining an address register. address: (# streetName: @text; streetNo: @integer; city: @text; print:< (# do inner; streetName->puttext; streetNo->putint; (*etc.*) #); #); addressRegister: register(# element::< address #) We may use the address pattern for defining part-objects of employee/company objects: employee: (# name: @text; {the name of the employee*) adr: @address(# print:: (# do name->puttext #)#) #); company: (# name: @text; (*the name of the company*) adr: @address(# print:: (# do name->puttext #) #) #); The object adr of employee is defined as a singular address object where the virtual print pattern is defined to print the name of the employee. As can be seen it is possible to define a part-object and define its virtual procedures to have an effect on the whole object. The company pattern is defined in a similar way. It is possible to handle the address aspect of employees and companies. An example is an address register: AReg: @addressRegister; employee1.adr[]->AReg.insert; employee2.adr[]->AReg.insert; company1.adr[]->AReg.insert; company2.adr[]->AReg.insert; AReg.scan(# do current.print #) The AReg register will contain address objects which are part of either employee objects or company objects. For the purpose of the register this does not matter. When the print procedure of one of these address objects is invoked it will call the print procedure associated with either employee or company. The scanning of the AReg register is an example of invoking the print pattern. The example shows that in BETA inheritance from part-objects may be used as an alternative to inheritance from superpatterns. The choice in a given situation depends of course on the actual concepts and phenomena to be modeled. In the above example it seems reasonable to model the address as a part instead of defining employee and company as specializations of address. In general it is possible to specify multiple inheritance from part-objects since it is possible to have Inheritance from part-objects 77 MIA 94-26: BETA Language Introduction several part-objects like the address object above. This form of multiple inheritance provides most of the functionality of multiple inheritance from C++ and Eiffel. It is simpler since the programmer must be explicit about the combination of virtual operations. It does, however, not handle so-called overlapping superclasses. The programmer must also explicitly redefine the attributes of the component classes. This may be tedious if there is a large number of attributes. However, a renaming mechanism for making this easier has been proposed for BETA, but it is not yet implemented in the Mjølner System. Multiple inheritance from part-objects should be used when there is a part-of relationship between the components and the compound. This also covers situations where implementations are inherited. It should not be used as a replacement for multiple specialization hierarchies. A common example of using multiple inheritance is modeling windows with titles and borders. This may be modeled using block-structure. Since a window may have a title, a border or both, the following class hierarchy using multiple inheritance is often used: In BETA this can be described using nested patterns: window: (# title: (# #); border: (# #); #); aWindow: @window(# T: @title; B: @border #) The descriptions for title and border are made using nested patterns. For a given window, like aWindow, a title object and a border object may be instantiated. If e.g. two titles are needed, two instances of title are made. This example illustrates another situation where multiple inheritance may be avoided. Beta Language Introduction Inheritance from part-objects Mjølner Informatics 78