• Ingen resultater fundet

The inheritance mechanism is the mechanism which allows a mixin which is the

head of a pattern to use attributes declared in the tail of that pattern; since the tail of a pattern is itself a pattern, this description applies recursively to all the mixins in patterns. The word `inheritance' reects the fact that those attributes are in a way given to the mixin at the head by the ancestors (superpatterns), because all sublists of the tail are superpatterns.

The syntactic representation of the inheritance mechanism is the ObjectDe-scriptor, which is the full version of the simplied syntactic constructDescriptor which was specied in Fig. 2.1 on page 23 and Fig. 2.3 on page 26. The main dierence is that theNamein front of theMainpartin aDescriptoris more ex-ible in the real ObjectDescriptor, allowing more dierent kinds of superpattern specications. However, the Descriptor will suce for the presentation of the mechanism here, so we will stick with the simplied syntax. The full syntax can be found in App. A.

Like in Sect. 2.2, the class-like aspects and the method-like aspects of the semantics of Descriptors are treated separately, with this section describing in-heritance of attributes and Sect. 3.8 describing specialization of behavior.

A description of inheritance would typically talk about the attributes which a given object or class has. To be able to do this we must make it reasonably precise what `has' means in that connection. This will be done in three phases:

First the notion of attributes in part objects and mixins is treated, then the attributes of an object or pattern as a whole can be dealt with, and nally the notion of a view on a pattern or object is introduced. Since the view determines what attributes can actually be used, all these concepts need to be brought together in order to describe what a given mixin inherits from its superpattern.

For a given part object, the available attributes accurately reect the at-tributes specied in the mixin from which the part object was originally created.

So we only have to consider the attributes specied in a mixin, and they accu-rately reect the syntactic declarations given in the MainPart associated with the mixin, with all name applications interpreted according to the run-time environment which is provided by the origin of the mixin.

This interpretation of names is xed at compile-time, where the static

ysis annotates every name application in the program with a specication of

3.6. INHERITANCE AND AVAILABLE ATTRIBUTES 59 how to nd the entity denoted by that particular name application. This spec-ication is called a run-time path. For any given run-time environment, the denoted entity can be accessed by mechanically following the instructions in the run-time path. For example, an attribute used as a parameter for a (pattern used as a) method would often be located in the same part object as the one which contains the name application that refers to it. In that case, the static analysis would annotate the name application with the empty run-time path, meaning it is right here!. If the method accesses an attribute of its enclosing object (which per denition includes a part object which is the origin), then the run-time path would start with one step outwards, and then possibly one more step in order to go to the right part object.

In summary, each part object contains attributes as specied in its mixin and using the static analysis annotations to give semantic meaning to the syntax on the right hand side of declarations. In this sense, a part object and a mixin never inherits anything! However, attributes in tail mixins are available according to the static analysis of them, and for that we must consider the set of attributes oered by complete patterns.

The fact that every name application during static analysis is annotated with a xed run-time path implies that every name application is statically associated with one specic name declaration. This is called static name binding, and it has some important implications, which are discussed near the end of this section.

The notion of inheritance only makes sense when applied to complete objects and patterns. Since the set of attributes of an object is determined fully by its pattern we only need to consider attributes as specied in patterns. However, we need to remember the connection between objects and patterns because an object may be specialized dynamically, hence becoming an instance of a more specialized pattern. More about this in Sect. 7.3.

The attributes of a pattern are simply the attributes of all the mixins in the list which is that pattern. However, some of these attributes may have the same name, causing a name clash. This is a classical problem with multiple inheritance [59], but it also occurs when a mixin declaring a given name N is applied to a class which already declaresN. It is our opinion that name clashes must be handled gracefully, since it is inappropriate to require that separately developed code which is brought together (by merging, e.g.) must use disjoint sets of names. Separately developed patterns should be capable of being merged and then used as each of the contributors with exactly those interactions that are appropriate for the modeling task. E.g., if a pattern is created by a combination ofVoterandEmployee, then it should be possible to use instances of it asVoter or as Employee, without confusing the contributions from each aspect, but also with unication of those contributions which should actually be considered the same. This ideal might not be fully achievable, butgbetadoes much to approach it. There are two possible reactions to name clashes. One is to consider dierent declarations of the same name as the same attribute, another is to consider them as dierent attributes. In gbeta (andBeta) these possibilities are both supported, and the choice is made explicitly by the programmer. However, not

all kinds of attributes can be unied (considered as the same).

Attributes with the same name in dierentMainParts are generally consid-ered distinct, so an object may contain, e.g., two attributes namedx, one being anintegerobject, and the other being a variable pattern, as in this example:

p: (# x: @integer #);

q: (# x: ##object #);

r: p & q; (* brings together two attributes named 'x' *) Ex.

3-4

Attributes of dierent kinds will never be considered the same, and for attributes of the same kind, only pattern attributes may be declared in multipleMainParts and be considered the same.

This is marked by making the pattern attribute virtual. As shown in Fig. 2.2 on page 24, virtual pattern attributes are associated with three dierent kinds of attribute declarations. One of them, the virtual pattern declaration (with

kind '<' as in X:< Point) introduces the attribute. Every virtual further-binding and nal-further-binding is statically associated with one particular virtual pattern declaration, so there are exactly as many pattern attributes associated with virtual declarations of all kinds in a pattern as there are virtual pattern declarations. The virtual pattern declaration is that pattern attribute, and the further- and nal-bindings modify the value of it. Virtual patterns are covered in more detail in Chap. 4.

An object attribute, or a variable (pattern or object) attribute can not be unied across mixins, but the obvious semantics of unifying such attributes is exactly what is achieved by introducing an auxiliary virtual pattern V, and then exploit the unication of V; the object specication or variable attribute qualication should then beV. Since unication must in all cases be declared explicitly, this is only slightly less convenient than having attribute unication available for all kinds. However, unication of dierent kinds of attributes (e.g., a pattern and an object) is not supported, cannot easily be obtained by a re-write, and does not have an obvious, meaningful semantics. As an example of the by-virtual-pattern unication of variable object attributes, consider the following:

link: 1(# v:< object; value: ^v; next: ^=this(link) #);

intlink: link2(# v::integer #); Ex.

3-5

Details of virtuals are deferred to Chap. 4, but the example is hopefully un-derstandable with the following explanation. In this example, thelinkpattern implements a basic singly linked list, which uses the virtualvas the qualica-tion of the variable object attribute value. In intlink, which is [2;1] using the given numbering of MainParts, the unication of the attributevin the two mixins ensures that the qualication of valuein anintlinkisinteger. This is actually a useful semantics which might very well have been the meaning of unifying variable object attributes. However, since this is an easy re-write, there is no need for such unication.

3.6. INHERITANCE AND AVAILABLE ATTRIBUTES 61

1 (# cell: (# v:< object; value: ^v #);

2 integerCell: @cell(# v::< integer #);

3 touchyInteger: integer

4 (# enter (# 'Somebody changed me!'->stdio #)#);

5 myCell: ^cell

6 do

7 touchyInteger[]->integerCell.value[];

8 integerCell[]->myCell[];

9 (* for myCell.value we now have different view,

10 * qualification, and pattern of referred object *)

11 #)

Figure 3.5: Views, qualications, and actual patterns

The result is that a pattern has exactly those attributes which are declared in the MainParts of the mixins in it, except for the virtual further- and nal-binding declarations (which are statically associated with some virtual pattern declaration but do not count themselves). Since some declarations may have the same name without being unied, there is still the problem of ambiguity when looking up such a name. To handle this we need dierent views on objects.

Actually, for backward compatibility with Beta, an attempt to access an attribute of nameN where two or more attributes of the nameN are declared will succeed, and it will access thatN attribute which is declared in the front-most (front-most specic) mixin. However, this is deprecated and a warning will be generated during static analysis. The right solution is to use a view which se-lects a suitable superpattern that does not contain the name clash. This will probably improve the readability of the code anyway.

A view on an object is a statically known pattern which is used to access the object.4 Such a pattern may be derived from the specication of an object attribute, it may be the qualication of a variable object or pattern, or it may be denoted in a qualied attribute denotation (an upward cast, with syntax QualiedAttrDen which can be found in the grammar, App. A, and which is presented in Sect. 8.2.2). In all cases the view is the contract between the object being accessed and the client or user of the object which performs the access. The view determines what attributes may be accessed and what properties those attributes have themselves.

In many cases, for instance with a virtual, the pattern on which a view is based is not known exactly at compile-time, but there is always a statically known upper bound. This upper bound is then used for the view. It is noted in the static analysis that the view is possibly incomplete, i.e., that the pattern may be strictly more specialized than what is statically known. This means that

4Since patterns do not exist before run-time there is no such thing as a compile-time constant pattern, but often a pattern is completely known statically relativetothe current

object

a certain list of mixins is statically guaranteed to be present, but the actual run-time qualication may contain additional mixins.

Note that this is not the same as subtype polymorphism. Where subtype polymorphism is concerned with the relation between, e.g., the qualication of a variable object attribute and the pattern of the object referred by it, this is about the relation between the qualication and the static knowledge about the qualication. In a language where classes are always compile-time constants, there may be subtype polymorphism, but still each qualication will be known exactly during static analysis.

Figure 3.5 illustrates the dierent patterns involved in this situation. It uses virtual patterns which will be explained in detail in Chap. 4, but at this point we just describe the properties of the concrete example. The cell pattern describes objects which may hold a value qualied by the virtual attribute

v which is object in cell, and the integerCell object further-binds v to

integersuch that thevalueinintegerCellmust be qualied byinteger. A

touchyIntegeris a specialization of integerwhich will complain whenever it is being assigned a new value. Line 7 and 8 in the gure then initialize thevalue of integerCellto an instance of touchyInteger, and makes myCellrefer to

integerCell. As a result, in line 9, the variable object attributemyCell.value

has objectas the view pattern (because the view of myCellis cell); it has

integer as the qualication, because myCell refers to integerCellwhose v attribute has the value integer; and the object referred bymyCell.valueis an instance oftouchyInteger. This makes the view, the qualication, and the pattern of the referred object dierent, so these concepts are obviously distinct.

The static analysis and semantics of gbeta ensures that certain invariants hold. For instance with a variable object attribute, the view pattern will at all times be a superpattern of the qualication, and the qualication will at all times be a superpattern of the pattern of the referred object. These in-variants are necessary and sucient to ensure that attribute access will never fail (there are never any MessageNotUnderstooderrors). However, note that if the view is dierent from the qualication, reference assignment (like :::

->myCell.value[]in the example) is and must be detected as type unsafe, unless relative information otherwise guarantees the type safety.

Finally, as promised, we will discuss some important implications of the fact that gbetahas static name binding. It allows programmers to look up

bly by double-clicking::: ) what declaration any given usage of a name refers to, and hence matches very well with locational (or stricter) equivalence: The name application may unconditionally take over whatever information can be obtained about the associated declaration, including comments and general knowledge about the intended usage and properties of entities near that loca-tion; the programmer may then rest assured that this knowledge can not be invalidated by accidental name clashes or similar phenomena which can only be detected using global knowledge.

Moreover, the static name binding ensures that certain dynamic operations are safe, such as dynamically adding new part objects to an existing object, thereby giving it new attributes. Luigi Liquori says in [63, p. 150] that this is

3.7. PATTERN MERGING 63