• Ingen resultater fundet

7.3 Dynamic Specialization of Objects

7.3.1 Change of Class in Various Languages

hearStory:

3(# theKing: ^king do :::

avidGolfer## -> theKing##;

#) Ex.

7-5

The eect of this assignment is that the object referred bytheKingis modied such that it becomes an instance of king&avidGolfer, i.e., an instance of the merge of the previous pattern of the object and the new patternavidGolfer. As a result, theKing will be enhanced with new part objects for whatever is missing in order to make him an instance of some subpattern of avidGolfer; the change in terms of concrete mixin lists would be from [2;1] to [4;2;1]. Note that the addition of new part objects may also lead to a change in the value of virtual attributes; the object will be the same, but it will in every way be enhanced such that it is really an instance of the new, more special pattern.

We believe that it is both a very useful and a quite understandable facility to be able to create objects with a certain structure and then gradually specialize that structure by adding new aspects as they are discovered.

7.3.1 Change of Class in Various Languages

One invariant that most object-oriented languages maintain for objects is that they come into existence with a given structure (interface and implementation) and then do not change that structure ever after. For class based languages it may be phrased as the invariant that each object is an instance of a class, the class is immutable, the class determines the structure of its instances, and an object does not change its class. In gbeta, the last of these restrictions is lifted.

Some languages come in implementations that are complete in the sense that they include the tools needed for creating and modifying programs, for example Smalltalk and Self. This means that it is indeed possible to programmatically change a class in Smalltalk resp. modify the structure of an object in Self.

However, this is considered programming, and it is not commonly used as a technique in ordinary programs. For example, it is stated that the protocol which supports the programmatic editing of classes `is not generally used by programmers, but it may be of interest to system developers' [50, page 283].

Moreover, after the editing of the class, there is still the question about what to do with existing instancesshould they all change? should they remain as instances of the old version of the class? etc. The programming environments

7.3. DYNAMIC SPECIALIZATION OF OBJECTS 147 handle these issues, but the languages are not oriented towards the widespread use of programmatic object structure manipulations.

However, making changes directly to the structure of an object is not the only way to make it act dierently. The rest of this section surveys a number of related mechanisms.

The language Self [114] supports a very general form of object behavior modication: In Self, an object may inherit from another object by having a so-called parent slot which refers to that other object. Parent slots may be assigned dynamically, and that works similarly to dynamically changing the inheritance graph, thereby potentially redening, adding, or removing inherited methods and state. There is support for type inference in Self [3], but this builds on a closed world assumption, so the entire program must be available for the inference algorithm, and changing one single line of the code would potentially invalidate any of the inferred types. Moreover, these types are intended to enable optimizations by discovering some invariants that programs actually support.

They are not intended to prove that declared invariants will be supported, e.g., there is no syntax by which such invariants can even be declared.

Smalltalk [50] is class-based, and every object is an instance of one particular class. However, the become: primitive swaps the identities of two objects and thus supports arbitrary structural changes to any given object identity. It re-mains a low-level task for the programmer to transfer any shared state to make a group of objects seem like one object with varying structure.

In a very sophisticated approach [80], Mira Mezini uses a so-called meta-Combiner object in a reective middle layer between objects and classes. Each object (let's call it complete) corresponds to one core object and a number of adjustment objects (let's call them internal), as well as the metaCombiner object which manages information about the methods of the complete object.

It is possible to add and remove adjustments. When two or more internal ob-jects implement a given method they can be treated as aspects of the same and executed sequentially, or they can be treated as unrelated and made available in separate scopes. This enables a programmer (who noticed the danger) to avoid accidental identication of methods that are conceptually dierent but have the same name.

Note that a complete object in the metaCombiner approach consists of a group of (traditional Smalltalk) objects, whereas an object in gbetaconsists of a group of part objects. In both cases the object is not monolithic, and this enables greater exibility. Furthermore, it would not be unreasonable to think of a Self object together with all objects reachable directly or indirectly through parent links as an object, and that again would often be a multi-part entity.

The language Sina embeds the concept of composition lters [113, 5] which also allow for very exible and expressive control over the method dispatch and state distribution within a collection of objects. The composition lters may reject or redirect message sends depending on dynamically evaluated conditions, and it is possible to simulate a standard inheritance mechanism, which may then select varying parents dynamically. However, Sina does not have a static type system.

An example of a very general kind of support for actually changing the struc-ture of an existing object is the change-classfunction in CLOS [56]. When the class of an object is changed, a system dened generic function is called which uses various heuristics (such as the spelling of slot names) to determine whether to transfer the value of a slot from the old state of the object to the new one, or to initialize a slot as in a new object. This mechanism provides the programmer with the ultimate exibility, but an unrestricted mechanism like this will of course potentially break any safety invariant that the run-time system might try to maintain, and is thus incompatible with any static analysis that attempts to guarantee thatMessageNotUnderstooderrors cannot occur.

All these systems are very exible. The exibility goes along with a very rich universe of potential program executions, and this makes it dicult to prove that any specic properties hold about individual program elementsin other words, they are not designed for static type checking.

The CLOS [56] convention of using some classes for mixin inheritance has been developed [10, 25, 100] into a separate concept of mixin-based inheritance.

For more information about mixins, please see Sect. 3.2. In [100, 68, 67] it is described how Agora mixins can support dynamic inheritance and how it can be statically type checked. The catch is that each object must contain specications of all its potential enhancements, which is not acceptable in practical software engineering.

In Cecil [19, 20, 21, 43], predicate objects have been introduced to support dynamic changes of object structure and method implementations. Cecil is prototype based like Self, but with a slightly dierent object model. An object has a xed position in the ordinary, static inheritance hierarchy, but it may also inherit from a number of predicate objects, depending on its state. Since predicate inheritance is determined by general boolean expressions any non-trivial questions are undecidable; but if the programmer manually proves (or claims) some disjointness and completeness properties and annotates the code, a type check can be made. Predicate dispatching allows for an automated check of the disjointness and completeness properties, but only insofar as the boolean expressions can be considered as black boxes (so it cannot detect that, e.g.,x>0 and x<=0are disjoint and complete). However, programs are only accepted as type safe if the dynamic inheritance provably has no eect on the interfaces, i.e.

if the dynamics may simply be ignored for type checking purposes.

Hence, in general, dynamic inheritance and genuine strict, static type check-ing have not been reconciled. Ingbetathere is support for dynamic creation of classes, inheritance from classes known only at run-time, and dynamic evolution of the structure of objects, without compromising the static type checking.

Compared to Self, the Smalltalk approaches, and composition lters, gbeta is less exible: The structure of objects may be enriched, not reduced. This means that an object may be specialized to an instance of a more derived pat-tern, not generalized to an instance of a superpattern or to an unrelated pattern.

Compared to the metaCombiner approach, adjustments can be expressed nat-urally, and the method combination of standard Beta is more static but also more expressive. Moreover, the Smalltalk problem of accidental identication of

7.3. DYNAMIC SPECIALIZATION OF OBJECTS 149 methods is irrelevant ingbeta, because of the static name binding. Compared to Agora, gbeta does not require that each object foresees its entire potential for structure development statically. Compared to predicate based inheritance, gbeta does not support changing the structure of an object automatically, an explicit statement must be executed. But unlike Cecil, the structure enhance-ments ingbeta may certainly change an object in such a way that it supports interfaces that it did not support before the modication, and this is handled in a type safe manner.

The next section details the mechanism behind dynamic specialization of objects ingbeta.