• Ingen resultater fundet

8.2 Convenience Constructs

9.1.2 Why Type Casing is Good

As argued in the previous section, the organization of the implementations of a range of behaviors for a range of dierent kinds of entities by type casing on the entities is not recommended in the typical case; but there are also other uses for type case constructs, and they motivate the introduction of such a construct in gbeta.

To see why these other uses of type casing are signicant we have to consider some possible trade-os in the management of static information, assuming that type casing is not available. Note that a reference assignment inBetaand gbetawhich causes a warning during static analysis and generation of a dynamic qualication check is in fact a special case of type casing, where the two cases are:

(1) perform a reference assignment, or (2) raise a run-time error. Consequently, we must assume that such unsafe reference assignments are also not available in the following discussion, except for Smalltalk where the maintained static information is incomplete and we might say that such implicit type cases are present in every message send.

The maintenance of static knowledge about program execution entities is not a free lunch. As an example of a radical trade-o in this respect, the lack of explicit typing of instance variables in Smalltalk make it possible to change and recompile a classC in a system without aecting other classes thanCand its subclasses, and without considering any other classes than the superclasses ofC (and that is only to nd the slot numbers of inherited instance variables).1 This approach basically reduces the static knowledge about run-time entities to a minimum, thereby gaining exibility and discarding safety.

At the other end of the spectrum we have a language like Pascal, where the strict typing of all entities ensures that the information about the structure of a given entity is propagated at least as far out into the source code as that kind of entity itself. This approach insists on complete static information about all accessed entities, thereby guaranteeing safety, but paying for it with a vastly increased network of program part interdependencies.

The complexity of global interdependencies tends to grow faster than pro-portionally when systems get larger, so the cost of maintaining complete static information gets gargantuan with complex systems. Moreover, each global con-cern has to be considered again in every new context where it is injected, so a module which carries a global concern with it may be a very heavy burden if it is to be integrated into a large system.

1Self has an even more radical separation in principle, but the very sophisticated com-pilation techniques reintroduce many globally scoped interdependencies; similar phenomena probably occur with highly optimizing modern implementations of Smalltalk.

9.1. TYPE CASINGTHEWHENIMPERATIVE 177 Statically type checked object-oriented languages support the notions of sub-typing and polymorphism, i.e., an attribute may have the safety invariant that it always refers to an entity of a given type, and a type may be dened to include objects which are instances of a given class or any of its subclasses, so a given name may give access to various dierent but related kinds of entities. This allows for intermediate trade-os, where the static information about a given entity is not completely generic like in Smalltalk, and not rigidly complete like in Pascal.

An important reason why it is sucient in many cases to have incomplete information about the structure of an object is that dierent method imple-mentations may perform a range of actions with the same meaning using late binding. So, polymorphism and late binding play together to make unknown parts of an object structure usefulor to allow useful parts of an object struc-ture to be unknown.

However, that approach can only be used as long as there is a way to express the desired interaction with a given object in terms of some sequence of invo-cations of methods which are known to be supported. The situation is dierent if we do not know exactly what object to use, such that we cannot invoke the desired method before we have found a suitable object. Similarly, we may need to obtain additional information because an object which is already at hand might support certain methods that we need to use. Note that this discussion is not about subversion of information hiding and ADTs (abstract data types), it is about retrieving information which is public and relevant, but currently unavailable in the given context.

In particular, for a method M to be invokable at all on a given object O, there must exist a reference to O somewhere in the program execution whose statically known type includes knowledge about that methodM(note that this might also be athis/selfreference, possibly implicit). Whether or not there exists at least one reference to a given object with a given type is a global considerationjust like garbage collection, which is concerned with a similar question. It is well-known that the global strategies which are needed in order to manage memory reclamation when garbage collection is not availablefor instance conventions about ownership of objectsare the cause of a severe increase in overall system complexity and interconnectedness.

A typical example where it is hard to maintain the complete static knowledge about an object is when it is stored together with similar objects:

(# vehicle: (# ::: #);

car: vehicle(# ::: #);

bus: vehicle(# ::: #);

theCompany: @

(# theVehicles: @list(# element::vehicle #);

findBus: (# theBus: ^bus do ::: exit theBus[] #)

#)

#) Ex.

9-1

The challenge in this example is to write the implementation of the findBus method, which is supposed to deliver the identity of somebuswhich is associated

with theCompany. The vehicles which are associated with theCompany are conceivably acquired over a long period of time and under various dierent circumstances, and it is consequently hard to keep track of where eachvehicle came from and what kind of vehicle it is. This is an example where it is tempting to throw away static knowledge about each individual vehicle, and that is exactly what we are doing if thosevehicles are only stored intheVehicles, as in the example above. In that case, we cannot implementfindBusbecause there is no readily available reference to such a bus which has bus as its statically known qualication, and indeed there may not exist such a reference at all. We consider several solutions below, some of which will use more than one attribute in place of the singlelistcalled theVehiclesabove.

It is possible to introduce a virtual method returnThisIfBusin vehicle and implement it such that it delivers the identity of the object qualied as a

busif it is abus, and deliversNONEotherwise. That is a bad solution for two reasons. One reason is that this style in general requires changes to vehicle whenever we add a new subpatternX of vehicle that we might want to call such areturnThisIfX method on. This is a global dependency loop because it makes vehicle depend on all its subpatterns in the general case. Another reason is that this amounts exactly to a manual reconstruction of the type casing mechanism that we are consideringif we really needreturnThisIfXmethods then it would be better to add support for type casing, which does not cause additional global dependencies.

An alternative strategy could be to introduce separatelists ofbuses and of othervehicles, but that would only work until we need agetCarmethod, then we would need to maintain alistofbuses, alistof cars, and alistof other

vehicles. We could put each kind of vehicle into a separatelist, but then we would need to changetheCompanyevery time a new subpattern of vehiclewas addedexactly one of those global dependencies that we want to avoid.

To conclude, it may be a noble goal in the name of safety and correctness to preserve full static knowledge about all run-time entities somewhere in the system, and even maintain enough knowledge to know where to go to nd a reference which oers sucient static information for a given purpose. But we do not think that this is realistic in large and complex systems, or it will incur severe disadvantages in terms of increased complexity. The complexity cost can even be disproportionately largeif a module is to be reused in a complex system then any global concerns that it carries with it (such as keeping track of the number of references of a certain kind) will tend to be as complex as the total system to maintain, not just as complex as the module.

Finally, the tendency to lose static information about objects over time is of course aggravated even more if the scope in time or space is widened, for example for objects which are stored in a database and retrieved in the execution of another program, or objects which are exchanged or just accessed across a network.

Hence, it is sometimes justied to recover information about the type or structure of run-time entities, i.e., to do type casing.

9.1. TYPE CASINGTHEWHENIMPERATIVE 179