| ||
Modularization: A Concrete Example (continued) |
In the last section we promised to explain how the asString method can be provided even though the declarations of the ordered patterns and its subpatterns contain no such method. An important precondition is the declaration of the slots named OrderedLib, TextLib, NumberLib, IntLib, and FloatLib. These slots are of the syntactic category attributes. This means that we can provide attribute declarations to these patterns by means of these slots, thereby, e.g., adding new methods to the patterns. That is exactly what we need to do. Note that there is nothing new in this compared with the fragment system in the language BETA; however, there is a difference between the (ideal) definition of the fragment system and the actual implementation in the Mjolner BETA System, so the following would not compile under Mjolner BETA, but it is supported in the implementation of gbeta. So, to return to the concrete example, we add an extra method to many of the patterns, so we can obtain a printable representation of a given ordered entity:
In order to keep the example short and complete we have skipped over the problem of printing numbers and just return the string <anInt> for all int objects and <aFloat> for all float objects. With these definitions the example is complete. It is worth considering the type properties of this inheritance hierarchy. Usually it would give rise to typing problems to have both text and number derived from ordered. The problem is that if ordered is an ordinary class then this implies that any two ordered objects can be compared, and this is probably not meaningful for a text and a number; we would like to be able to compare numbers and texts with their own kind, but the type system ought to complain if the two groups of ordered things are mixed. To obtain this level of correctness check we could use something else than a class for ordered. In languages with parameterized classes (C++, Eiffel, Cecil, GJ, Pizza, ..) it would be possible to make ordered a parameterized class. This normally means that text and number would become unrelated classes, and that would ensure that the two kinds of ordered objects would be kept separate by the type system. With F-bounded polymorphism and explicit co-variance and contra-variance declarations, as in Cecil, it is even possible to allow comparisons of subclasses of instances of ordered, such as int and float. However, it is not possible in these languages to refer polymorphically to an arbitrary ordered object and call such a method as asString. The problem is that ordered is not a class, and hence there is no way to declare a polymorphic reference (variable, field, pointer, whatever it's called in the given language) to such objects. The BETA and gbeta type systems handle this problem in a more general manner. The approach taken is not to uncritically make all the subpatterns subtypes (which leads to the confusion problem mentioned above), and it is not to make ordered a non-class (which leads to the cannot-call-asString-polymorphically problem), but to use computed types. Types in BETA and gbeta do not have an explicit representation in the program, they are all inferred. Patterns are explicit, but types differ from patterns in that they include a description of what properties all the possible patterns in a given context might have. One of the consequences of this is that it is recognized as safe by the type system to call asString on an object which is only known as being an instance of a pattern which is less than or equal to ordered. At the same time, the type system recognizes that it would not be safe to compare two objects of which the same is known. Comparison is only safe when the objects are both known to be less-equal than number, or when they are both known to be less-equal than text. (Note that the final bound on cmpType in number and in text plays a crucial role in this connection). In this approach, individual methods are marked as "OK" or "Nope, not safe" to call by the type system, depending on the level of knowledge about the involved objects. Compared to the approaches where types are explicit (and generally constant as seen from all points in the program), this approach based on computed types allows for a greater freedom for programmers, because more of the safe actions are recognized as safe by the type analysis. The dynamically typed approaches (e.g. Smalltalk) also allow all the safe actions, but that is just because everything is allowed, including such things as comparing a text with a number, which is, rightfully, caught as a type error in all the statically typed approaches. As expected, the patterns in our example have the desired type properties: number is specialized to int and to float, and the final bound on cmpType in number ensures that all kinds of numbers can be compared. Similarly, text objects can be compared. However, if a text and a number are compared with each other, the static analysis will complain that this is not typesafe, as it should. Finally, the method asString can be called on any ordered object, even if we do not know whether it is a text, or a number, or yet another subpattern of ordered. A more detailed presentation of these issues is given in the PhD thesis. |