• Ingen resultater fundet

is that a pattern on the form [mk;mk 1:::m1] in Beta only has the pat-terns [mj;mj 1:::m1] for j 2 f0:::kgas superpatterns, no other sublists of [mk;mk 1:::m1] can even exist, and hence a given behavior can never be in-serted into another behavior by specialization inBeta, it can only insert the incremental behavior as leaves in the syntax tree completion.

Finally, by comparing a with b&a, c&a, b&c&a, and c&b&awe can observe the traditional specialization of the behaviors of b,c,b&c, andc&bwithaas a new leaf behavior. Of course, even though this kind of behavior specialization is possible in Beta, it would require the textual copying of the denition of a into every position where it should be used to specialize a behavior.

3.9 Object Creation

Objects do not just magically come into existence ingbeta. Actually, the process of creating an object is complex, even though there is no notion of `constructors' ingbeta. The following description details what should happen at the conceptual level in any gbeta implementation. It also describes the current (slow, but general) method used in the existing implementation of gbeta. However, a high quality implementation of gbetawould exploit the information from static analysis to pre-compute or avoid the need for many entities which are actually created at run-time in the current gbetaimplementation.

As described in Sect. 3.3, an object is a list of part objects, each part object is associated with the mixin from which it was originally created, and each mixin is associated with aMainPartwhich contains a list of attribute declarations. The part object must have the attributes dened in the MainPart, and the run-time paths (presented in Sect. 3.6), which have been initialized during static analysis, are used to nd the entities (objects or patterns) denoted by the right hand sides of declarations, and that often makes it possible to initialize the attributes of a given part object.

However, it is not that easy in the presence of recursion. The problem is that an object may contain an attribute which denotes an entity which must be looked up in another attribute of the same object or even in a directly or indirectly nested object. In other words, the construction of the object cannot be completed without using (parts of) the object itself. The approach used to break this cycle in the current gbeta implementation is to compute attributes on demand, annotating each attribute which is being computed as half-done.

Whenever the object creation algorithm looks up an attribute and nds that it is half-done, a circularity must have been encountered and a run-time error is raised which kills the thread in which this failing object creation was going on.

A traversal of all attributes ensures that the demand occurs for every attribute during the object creation, whether or not it is used in the initialization of other attributes.

In many cases it will be possible to determine statically whether or not such an on-demand computation will give rise to circularities, but with the generality

of Beta(hence alsogbeta) it is possible to create circularities which cannot be detected without ow analysis, i.e., it is in general an undecidable problem to detect such cyclic dependencies. It would still be nice to detect a large number of easy, safe cases and then give warnings for the few, hard cases. An example in

Betacontaining a cycle which requires ow analysis to detect is the following:

p: (# r1: [(# exit r2.range #)] @integer;

r2: [(# exit r1.range #)] @integer

#) Ex.

3-8

This example contains some constructs which have not yet been presented, but they will be explained for this special case here. The patternpcontains two repe-tition attributes, i.e. two arrays of objects. The lengths of the reperepe-titions are de-termined by the evaluations(# exit r2.range #)and(# exit r1.range #), and these expressions must be evaluated before each of the repetition attributes can be created. Since the length of r1can only be computed whenr2has been initialized and vice-versa, there is no way to create an instance of p. It would be possible to solve the equation by choosing an arbitrary non-negativeinteger and initialize bothr1andr2with that length, but an implementation of gbeta is not required to discover such possibilities. The cycle should be detected and rejected, preferably at compile time, even though that is not possible in the general case. In the current implementation of gbeta it is never detected at compile time, so in particular this example gives rise to a run-time error.

Cyclic dependencies are not the only things to consider when creating new objects. In gbeta, the approach was taken to allow many things which are prohibited inBeta, in order to try out whether or not the increased generality and exibility would be worthwhile.

As an example, all kinds of entities may be used on the right hand side of declarations, so for instance a variable object have a variable pattern as its qualication (declared type). Since a variable pattern may be NONE, there may arise a run-time error already during initialization of the attribute.

However, even if the variable pattern is not NONE, some decisions must be made with respect to the semantics of this variable object later. The problem is that the variable pattern may change during the life time of the variable object.

If we have the following situation, what would be the correct reaction?

1 (# vp: ##object

2 do string##->vp##;

3 (# vo: ^vp

4 do &string[]->vo[];

5 integer##->vp##;

6 ..

7 #)

8 #) Ex.

3-9

In this example, vpis a variable pattern with qualicationobjectwhich hap-pens to have the value stringat the point where the inserted item is created, i.e., when line 3 is reached. In line 4, the assignment makes the variable object

vorefer to a stringobject. Since the value of the qualicationvp is string

3.9. OBJECT CREATION 75

The snapshot principle:

The right hand side of an attribute declaration is evaluated when the enclosing object is created,

and it is never changed after that

Figure 3.10: The principle behind variable entities used in declarations that would be ne. In line 5, however,pvis changed by the assignment to have the valueinteger, and at that point the variable objectvowill no longer refer to an object which is qualied by the value of pv.

It is obviously not manageable to have a qualication of a variable object which may change like that during the life time of that variable object. In general, it is not desirable that attributes may silently become invalid, so qual-ications should be immutable. One rule which will make them immutable is the conservative rule that qualications must be compile-time constants (rela-tive to the current object), but this was exactly the limited universe from which we wanted to escape. Another rule which is very simple and which handles all the new, dynamic cases consistently is the snapshot principle which is shown in Fig. 3.10. This principle is used in gbetafor dynamic entities used on the right hand side of declarations. Note that it applies equally well to the well-known static cases, even though it makes no dierence there.

Also note that the semantics where only compile-time constants are allowed on the right hand side of declarations is a special case of the snapshot seman-tics, which makes it possible to prohibit any or all of the dynamic enhancements provided by the snapshot principle as desired. One aspect of traditionalBeta semantics which points in the direction of the snapshot principle is the seman-tics of length specications in declarations of repetitions (arrays). Such an expression, like x in `r: [x]^boolean', is evaluated when the enclosing object (of which `r' is an attribute) is created, and the length of the repetition is of course not adjusted every time the current value of the expression x changes.

That is an example of a snapshot of the state of the program being used to determine the meaning of a declaration.

Another example that we have already seen earlier is the support for constant references by coercion, as described in Sect. 2.3.4. An example which will be covered later, in Sect. 7.2, is the usage of a pattern variable as a superpattern, to create a dynamic control structure.

Reconsidering the example above with the declarationvo: ^vp, it may ac-tually be useful: The declaration gives voa qualication which is only known by upper boundsince vp is less-equal than its own qualication, the quali-cation of vois also less-equal than that bound. This means that an object can safely be obtained fromvowith the qualication of pvas the statically known pattern, but any attempt to reference-assign to vo, e.g. m->vo[], will cause a message from static analysis that this is not type safe. On the other hand, it

Local lookup of a name

N

in a view

P

:

If

P

is

Object

then the lookup fails; otherwise if the head of

P

contains a declaration of

N

, then that declaration is the result; otherwise the result is the lookup of

N

in the tail of

P

Figure 3.11: The rule for local lookup

is possible (i.e. statically type safe) to renew vo, i.e., to create a new instance of the qualication of voand makevorefer to this new object. The syntax for this is &vo (or &vo[]if we want to prevent execution of the new object). As a result we have obtained a variable object attribute which is read-renew, as opposed to read-only or read-write. Client code may use voas an instance of the qualication of vp(we might call this the ocial qualication ofvo), and every instance of the enclosing object can have its own secret qualication for

vo, determined by the value of vp at snapshot time. By the way, this is an example of a context dependency; see in Chap. 5 why that is actually a natural and justiable thing to use, when used right.