• Ingen resultater fundet

7.2 Dynamic Patterns

7.2.2 Dynamic Control Structures

Figure 7.1: A user-dened control structure

never be a loop in which every client is waiting for the next client to release a resource, because such a loop would have to contain a link where a client with access to Ri is waiting for another client to release Rj for some i > j. That client would violate the ordering rule. Clients may release their access to resources in order when they are not needed any more.

Since the choice of ordering may greatly aect the overall performance of the system, it may be benecial to change the ordering dynamically.1 There may also be other disciplines than the ordering rule which may be more appropriate at times, for instance an optimistic approach when the load is not too high.

All it takes is that all methods which need access to some of the resources R1:::Rn must have added the concurrency control mixin du jour on top of it.

The modied method would then be invoked with all the usual arguments and would return results as usual.

With this approach, a method, procedure, or function is not just something that may be invoked, it is instead something which may be passed around and modied, and it may then be executed zero or more times. That is probably a change in programming style for most programmers, but we believe that it is a path worth exploring.

7.2.2 Dynamic Control Structures

There is a special case of usage of dynamic patterns that deserves separate treatment. Technically it is simply the case where a single mixin is added at the bottom of a given pattern, i.e., as the most specic mixin. That is just like ordinary single inheritance, except that the superpattern is not a compile-time constant. This section is about that case.

A control structure is a language entity (builtin or user-dened) which is parameterized with one or more pieces of code, bodies, immersed into a name space. A standard example is anif-statement in any language, where the bodies are thethen-part and theelse-part; in this case the name space is empty (no declared names are provided by theif-statement). A standard control structure which provides a non-empty name space is a for-statement, which typically allows the body to refer to an index variable which is incremented with each execution of the body.

1The transitions must be handled carefully, of course.

1 (# myFile: @file; (* an interface to a disk file *)

2

3 (* the iterator interface *)

4 iterator: (# theLine: ^text do INNER #);

5

6 (* concrete iterators *)

7 inputIterator: iterator

8 (# (* read lines from std. input until empty line *)

9 do (while (getline->theLine[]).length>0 do

10 INNER

11 while)

12 #);

13 fileIterator: iterator

14 (# (* iterate through the file, and make the

15 * current line available in 'theLine' *)

16 do (while (not myFile.eof) do

17 myFile.getline->theLine[];

18 INNER

19 while)

20 #);

21 filter: iterator

22 (# do (if theLine.length>0 then INNER if)#);

23

24 (* a method which takes an iterator as argument *)

25 LinePrinter:

26 (# anIter: ##iterator (* a pattern variable *)

27 enter anIter##

28 do (* iterate and print each text *)

29 anIter(# do theLine[]->putline #)

30 #)

31 do

32 inputIterator## -> LinePrinter;

33 'somename'->myFile.name; myFile.openread;

34 fileIterator&filter## -> LinePrinter

35 #)

Figure 7.2: LinePrinteris parameterized with a dynamic control structure,

anIter

The typical way to create a control structure inBetais to dene a pattern in which anINNERstatement is placed in the position where the body should be executed. See Fig. 7.1, showing the control structure twice which simply executes its body two times.

Ingbetait is possible to have dynamic control structures, using inheritance from a variable pattern. This makes it possible to parameterize a method with a control structure, delaying the decision about what control structure to use until run-time. Figure 7.2 is an example of this: the methodLinePrinter(line 25) accepts the argumentanIterwhich is some subpatternnot known before run-timeof iterator(line 4); anIteris then used as a superpattern in line 29.In line 32,LinePrinteris executed withinputIteratoras argument. This

7.2. DYNAMIC PATTERNS 143 will simply echo what the user types, line by line, until an empty line is entered.

Line 33 initializesmyFile, and in line 34LinePrinteris executed with another control structure as argument, namely the merge of fileIteratorandfilter. Thefiltercontrol structure (line 2122) executes its body iff theLineis non-empty. As a result, this invocation of LinePrinterwill print the contents of

myFile, line by line, but skipping all empty lines. We could also, e.g., compose with an iterator which visits each character of theLine; the composition would then visit each character of each line of the le.

Consider a similar example (with just one iterator, for brevity) in C++, as shown in Fig. 7.3. In this version,anIter(line 30) is an object, instance of a descendant of the classiterator(line 9);anItercannot itself be a class since classes are not rst class entities in C++, but this loss of generality does not aect this particular example.

The function call operator (line 11) is used to apply the iteratoranIterto the callbackbody(line 32). ThefileIteratorimplementation of the function call operator then provides itself as an argument to the given callbackcb(line 20), such that the implementation ofcbmay usetheLine. If we were to givetheLine as an argument tocbdirectly, then this and all other iterators would have to be changed if a new member, e.g.anotherLine, were added toiterator. Hence, this indirect approach is needed to obtain a similar stability towards changes of the control structure name space as thegbetaversion provides.

However, this is still an incomplete solution since iterators cannot be com-posed, likefileIteratorandfilterare composed in line 34 of Fig. 7.2. There does not seem to be a straightforward way to support iterator composition in C++. Note that composition of control structures is also not supported in the standard approach to iteration in C++ (especially in the Standard Template Library [88]). In this approach, an iterator objectspecic for the collection being iterated overis obtained and used in a for-statement, e.g.:

vector<int>::iterator i;

for (i = myVector.begin(); i != myVector.end(); i++)

cout << *i << endl; Ex.

7-3

This eectively standardizes a large group of control structures to be for -statements, but it does not support all control structures. E.g., a control structure which forks a separate thread to handle each step of the iteration could not be written in this style, because there is no way to write an iterator which changes the semantics of the actual control structure in use, namely the

for-statement.

The Sather [101, 87] concept of iters has the same limitation. A Sather iter is a co-routine which may only be executed lexically nested within a loop statement, and it may yield, in which case the loop execution continues, or it mayquit, which works similarly tobreakin C, terminating theloop. However, even though iters enable an elegant expression of many control structures, they cannot change the fact that the basic control structure is the built-in loop. Again, they do not support control structure composition.

1 #include <iostream.h>

11 virtual void operator() (callback cb) = 0;

12 char theLine[1000];

13 };

14

15 class fileIterator: public iterator {

16 public:

17 void operator () (callback cb) {

18 while (myFile) {

27 cout << iter.theLine << endl;

28 }

Figure 7.3: Using function pointers to simulate a dynamic control structure Yet another approach is used in Smalltalk, Self, Cecil, Dylan, and Tycoon-II, where a control structure like an if-statement is not built-in but instead uses late binding of methods in the objectstrueandfalseto obtain the choice between thethen-case and the else-case, and uses blocks/closures to defer the execution of the two cases. This approach does not build on one xed control structure, so it transparently allows the same kind of expression to invoke es-sentially dierent control structures. However, the name spaces that the bodies are immersed in are provided via arguments to the closures, so they have to be typed in for every usage. Moreover, changes in the number or types of names in these environments will generally require changes to all usage locations. In

7.3. DYNAMIC SPECIALIZATION OF OBJECTS 145