next up previous
Next: Acknowledgments Up: The Translator Pattern Previous: Implementation

   
Conclusion

We presented a general approach to add interpretations to data structures. Especially with regard to the definition of languages, for instance motivated by applications of the Interpreter pattern, our use of first-class functions nicely corresponds to the well-known formal method called denotational semantics. Our functions define local semantic definitions but also -- almost invisibly -- take care of traversing the source structure.

However, the Translator pattern may be applied in other areas as well. The IRIS Inventor toolkit [14] interprets three-dimensional scenes with a double-dispatch scheme relying on run-time type information too. Translator can define interpretations for CAD objects such as displaying, finite element analysis, and cost calculations. More generally, Translator may be used to assign semantics to STEP product data models [11]. Transferring only the semantics shields a client from the model as a whole. Incremental updates of the semantics makes remote interpretations (e.g., via the Internet) feasible.

We used the generic Function Object pattern in order to provide a mapping from heterogeneous elements to their corresponding translation functions. We, thus, avoided to demand that the source structure know about interpretations. This is valuable for adding semantics to already existing classes that can not or should not be changed. Also, this particular approach of external polymorphism successfully avoids the need to create a dependency cycle between source structure and new interpretation (as is the case with Visitor [7]).

Furthermore, we demonstrated the utility of homomorphic translations with regard to incremental evaluation. The specific nature of homomorphic mappings naturally allows for incremental updates. We introduced an intermediate structure from which the final semantics are produced. This nicely separates translation and semantics functions, provides a (non-intrusive) place for incremental results, and sometimes is useful for providing a logical structure for computed results.

Although the use of runtime type information is usually not recommended, we have shown it to be of great advantage in this case. Our emulation of multi-dispatch does not involve the disadvantages of the other approaches which we mentioned. Even beyond the capabilities of multi-methods in a statically compiled language, our generic functions may register new specialized functions at runtime. The possibility of runtime errors due to non-existent specialized functions is a nuisance, however.

It is recommended that one account for such situations with error handling and check the completeness of function packages manually.

As opposed to object-oriented decomposition, we used first-class functions in order to provide new interpretations. This might appear wrong to object-oriented purists. Note, however, that similar designs (the so-called Command pattern) are suggested and used by others as well [8,4]. In our case, issues like functional extension (avoiding to change all elements for new interpretations), handling heterogeneous interfaces, iteration of source structures, and non-intrusive addition of interpretations, could all nicely be dealt with, using the single concept of a generic (multi-dispatching) function.


next up previous
Next: Acknowledgments Up: The Translator Pattern Previous: Implementation
Thomas Kuehne
1998-07-06