• Ingen resultater fundet

7.3 Dynamic Specialization of Objects

8.1.2 The Type of this

InBeta, the name application which appears in the syntactic construct ThisOb-ject, e.g., Pointin this(Point), is used for two purposes. First, it is used to select one of the enclosing objectsthe nearest enclosing object whose pattern is statically known to be less-equal than the pattern of the attribute found by looking up that name. Second, the object which is accessed through such a ThisObject construct is accessed with the view of that name applicatione.g.

this(Point) will be treated like a Pointeven if it is a ColorPoint. To see what that means in practice, consider the following example:

1 (# (* This is in BETA, not gbeta *)

2 p: 1(# x: @integer #);

3 q: p2(# x: 3(# exit 2 #)#);

4 r: @q4(# do x->this(p).x #);

5 aP: ^p

6 do

7 r[]->aP[];

8 r.x->aP.x

9 #) Ex.

8-6

The topic of this example is how to access multiple attributes with the same name, in the same object. As mentioned in Sect. 3.6, this is deprecated in gbeta, but it might be inevitable, e.g., when combining two large, independently developed bodies of code. Whenever a single object has more than one attribute with a given name, the specicity of the mixins in the view determines which one of these attributes will be denoted by a given name application (statically as well as dynamically). In the example, the objectr, with part objects [4;2;1], contains two attributes named x, and the usage of xin its do-part will access the pattern in its part object 2 (line 3).

If there is a need to access another attribute than the chosen one, then we need to obtain another view on the object in which the desired attribute is the most specic one. For example, the construct this(p).x in line 4 is used to access thexobject attribute in part object 1 of r, line 2.

However, that approach does not work from outside of r, as in the main

do-part of the above example. Here,r.xin line 8 is used to access the pattern

x, but a separate variable object attribute such asaPmust be declared, line 5, in order to access the integer object x with aP.x, line 8. The two do-parts have the same eect, but they are too dierent. Moreover, it is confusing to have to perform a small data ow analysis to discover thatrandaPis actually the same object, only seen in two dierent ways.

For this reason, the view manipulation eect of ThisObjectwas removed in gbeta, and a construct similar to the

qua

expression in Simula was reintroduced

8.1. INCOMPATIBLE CHANGES 165 (see Sect. 8.2.2) to manage views, and to do that only. As a result, aThisObject construct in gbeta will be useful in all the same ways as in Beta when used to get access to one of the enclosing objects, but that enclosing object will be accessed under the view which represents the full available knowledge about that particular object. With this change, and the introduction of a

qua

-like

construct, two concerns have been separated, namely access to enclosing ob-jects and access to any object via a specied view. We think that this is an improvement for the readability of source code.

This is not the only benet. Another improvement is that theThisObject construct supports access to the entire known structure of enclosing objects, even if their patterns do not have a name, for example:

1 list:

2 (# element:< object;

3 scan: (# current: ^element do INNER #)

4 scanReverse: (# current: ^element do INNER #)

5 do scan

6 (# previous: ^element

7 do scanReverse

8 (# previous: ^element

9 do this(scan).current[]->previous[];

10 this(scan).previous[]->current[]

11 #)

12 #)

13 #) Ex.

8-7

This piece of code illustrates a problem with access to identically named at-tributes in enclosing objects. We can get access to this(scan).current(line 9) in bothBetaandgbetabecausecurrentis inherited fromscan(line 3), but only the gbetaanalysis allows us to get access to this(scan).previous(line 10). In this case the problem has been created articially in order to keep the example short, but this problem does arise now and then, and the ability to use the selected enclosing object in full viaThisObjectseems consistent and natural.

However, this change is actually insucient. Another generalization of ThisObject should be introduced, but this has not yet been implemented in gbeta: The name application is often not suciently exible for cases like the above example, for instance if the two nested control structure patterns are spe-cializations of oneList.scan and anotherList.scanwhich would be quite realistic. We would only need to allow a general AttributeDenotation instead of the NameApl which is currently allowed, such that we could write, e.g.,

this(anotherList.scan).current[]. It would also be convenient to allow

this alone, meaning this(object). The implementation would be easy, so this will probably happen soon.

Another case where thegbetaanalysis of ThisObjectis useful is when meth-ods return a reference to the enclosing object:

link:

(# T:< object;

value: ^T;

next: ^=this(link);

append:<

(# other: ^=this(link) enter other[]

do other[]->next[]; INNER

#)

#);

doubleLink: link

(# prev: ^=this(doubleLink);

append::< (# do this(doubleLink)[]->next.prev[] #)

#)

Figure 8.1: Linked lists

Counter:

(# x: @integer

inc: (# do x+1->x exit this(Counter)[] #);

dec: (# do x-1->x exit this(Counter)[] #)

#) Ex.

8-8

With this style (where methods return the enclosing object if they do not have to return anything else), it becomes natural to cascade method invocations like this:

(# ct: @Counter do

ct.inc.dec.inc

#) Ex.

8-9

This example also uses the enhancement described in Sect. 8.2.5 to avoid some parentheses, but the point we want to make here is that this breaks down with the approach taken inBetawhen we create a subpattern of Counter. Assume that we want to use cascading method calls on an instance of such a subpattern;

since the inherited methods havethis(Counter)[]in theirexitlists, the result will always be considered an instance of Counterwith theBeta analysis, and this means that the cascade is brokenafter the rst invocation of an inherited method, only the inherited methods can be used.

As a nal example illustrating how useful it is to be able to express a genuine

SelfType (a qualication which is recognized by the static analysis as being equal to the actual, run-time pattern of a given enclosing object), consider the

linkanddoubleLinkpatterns in Fig. 8.1. A linked list can be built fromlink elements, and a doubly linked list from doubleLink elements, with each link (double or not) carrying one value by reference via thevalueattribute.

8.1. INCOMPATIBLE CHANGES 167 Such a list will be heterogeneous in the sense that the value attribute of each linkmay refer to an instance of any subpattern of T. The virtual Tcan be further bound, hence restricting valueto be an instance of a more specic pattern than object; we may for example have alinkwhich requires that its

valueis aninteger, whereas anotherlinkmight require that its valueis a

boolean. The crucial point here is that this will never be the case for twolinks in the same list, and that is again because thenextattribute inlink(and also the prev attribute in doubleLink) have the exact qualication this(link). This exactness also ensures that there will never be a list which contains both some singlelinks and somedoubleLinks.

This means that every list which is built from instances of linkor a sub-pattern of linkis homogeneousin the sense that the spine of the list, the

links themselves, consists of instances of the exact same pattern. As a result, it is known to be safe to reference assign a new valueto an arbitrary element in such a list as soon as it is known to be safe to do it with just onelinkin the list. Consequently, the whole list is adequately constrained for static analysis by each of its elements, and that means that such lists can be passed around just by passing abound a reference to one of the elements.

This homogeneity also makesappendsafe: It would not be safe to assign both

other[]->next[]inappendinlinkandthis(doubleLink)[]->next.prev[]

in appendin doubleLinkwith inexact qualications. Moreover, if we changed the qualication of nextand prevto be an ordinary virtual then it would be possible to nal-bind it in all kinds of links which are used to create instances, and this could make usage of links from the outside safe, but it still would not help for the implementation of methods inside link. Hence, link and

doubleLinkcould not be implemented safely using virtuals.

To conclude, the combination of a trueSelfType and exact qualications make it possible to maintain a strict homogeneity in the patterns of objects in dynamic congurations of unbounded size, and this makes designs like link and doubleLink useful. Note that the typical design in Beta for situations like this is to have a wrapper pattern which contains a virtual pattern attribute which plays a similar role aslinkdoes here. A virtual pattern has the property that a usage of the name of the virtual inside its own denition will actually be a genuine SelfType(unless inheritance from virtuals is supported). However, with alistwhich is built on this principle (such as the standard MjolnerBeta

listpattern where the nested doubleLink virtual is calledtheCellType) it will not be possible to transfer links between dierentliststhey have a wrong origin for all otherlists than their enclosing one. This would be particularly nasty if a large list were to be reorganized, perhaps by splitting it into two smaller lists with roughly the same number of elements. That would require creation of new link objects for half of the elements in the large list.