This is an attempt to present ThomasKuehne's Translator pattern in a simplified form. You can get the real version from: http://homepages.mcs.vuw.ac.nz/~tk/publications/papers/translator.pdf
See FunctionalPatternSystemForObjectOrientedDesign
Often objects are organized into complex data structures such as trees or graphs in order to represent a system of relationships between the objects. Many times one of these structures will be useful in more than one context. For instance an abstract syntax tree would be useful in either a compiler or a pretty printer. In these cases the behavior of the structure and its components is extrinsic rather than intrinsic. That is to say that the behavior of each object in the structure has more to do with how the object is viewed by the context than with the nature of the object itself. When this situation occurs the symptoms include: relationships and behaviors that are correct for one context but not for another, polymorphic methods on the nodes that serve only a narrow subset of clients, overly complex traversal mechanisms that are intrusive or that require excessive book keeping. Ordinarily it is impossible to remedy this problem without violating the OpenClosedPrinciple because the definition of each object must change in response to its use in a new context.
Therefore:
Define a recursive traversal of the structure starting from some root node. Define a GenericFunction that takes a node of the structure as a parameter and recursively translates that node, all of its sub-nodes and all of the edges between them to an equivalent structure in the new context. A one to one correspondence between the elements of the original structure and elements in the resulting structure need not be maintained, but the translation must be localized to each node and be composable (i.e. a node should be able to implement its translation in terms of translations on its sub-nodes.) Implement the elements in the resulting structure so that the system of relationships that it contains is capable of supplying the desired services. Finally, perform the desired operation by traversing the new structure and calling the appropriate method(s) on each node.
Resulting context:
This two-part process has several advantages.
There may be an answer in the detailed discussion in the thesis. See ObjectFunctionalImplementation for discussion of attempts to implement the ideas. -- JohnFletcher
Compare with: VisitorPattern
Discussion:
Notes from ThomasKuehne
The Translator pattern is about translating one structure (e.g., an internal graph representation) into another structure (e.g., a data structure suitable for displaying aspects of the graph to a screen).
The main case for the TranslatorPattern is the observation that there is
Now, the TranslatorPattern realizes ExternalPolymorphism with a few interesting properties:
1. External behavior definition is completely non-intrusive with regard to elements.
2. There is an intermediate step before behavior is defined:
3. The translation scheme (HomomorphicMapping), uses local composable translations. These scheme makes it particularly easy to implement incremental evaluation of the target structure, i.e., quick recalculation of the external behavior in case only a small part of the source changed.
-- ThomasKuehne (edited for length by PhilGoodwin)
Grain of language
Disadvantage:
Some languages don't have GenericFunctions (or PatternMatching), so the programmer has to simulate them. AndrewAppel's Modern Compiler Implementation in Java recommends something similar to the above description of TranslatorPattern, but requiring the following sort of code for each `generic function':
translate(Expression node) { if(node instanceof ConstantExpression?) return translate (ConstantExpression? node) else if ... // additional clauses for each expression type }GenericFunctions actually use a map to do their dispatching, but the argument is still valid. TranslatorPattern may look odd and feel clumsy (even though it is a good solution to a real problem) in a traditional OO language. The TranslatorPattern is really a ProtoPattern (an invented rather than discovered solution) that is part of a pattern language invented by ThomasKuehne for the purpose of extending OO languages with functional concepts. Ultimately constructs like this would be supported more or less directly by a multi-paridigm functional/object oriented language. There is a growing trend toward looking to free functions in OO languages that support them (like C++). It is my opinion that such trends will ultimately lead to the adoption of a multi-paradigm approach such as the one that Thomas suggests. -- PhilGoodwin
The problem here seems to be the Java overloading semantics rather than lack of free functions. Java insists on resolving overloading at compile time. I seem to remember that CLOS would select the most specific applicable function at run time.
Does Smalltalk suffer from this? If memory serves, overloading isn't supported, and they get away with it due to the loose typing... it seems sometimes that however useful it may be, sometimes static typing just gets in the way :)
Is NiceLanguage the ultimate answer? No, although it does provide multimethods / generic functions & classes in a statically checked language.
It is possible to implement either the if-else version or the map version of the selector function using CeePlusPlus. See ObjectFunctionalImplementation. The map version is achieved using FunctoidsInCpp to make callable objects from the member functions to be called. It is also possible to implement a different version using the implementation of AcyclicVisitor described in ModernCeePlusPlusDesign. -- JohnFletcher
This may be a case of GreencoddsTenthRuleOfProgramming. If you have to keep re-writing data structures for different purposes, then perhaps a database is in order. --top
Perhaps in some cases, but not necessarily. TranslatorPattern is justified where a data structure that may be appropriate in one context is not appropriate in another, but both are needed simultaneously. (Note that I wrote "data structure", not "database". A "database" is built from data structures, and typically implies more than just data representation, such as persistence, ad-hoc querying and transactions.) For example, a game may maintain a data structure of game objects to support planning in the AI subsystem, but also needs to render the game objects to a graphical display device. What the graphical rendering engine expects as a data structure may be fundamentally different from what the AI subsystem expects -- which may be different yet again from how the overall gameplay logic expects to manipulate the objects. Use of TranslatorPattern makes it possible to maintain the game objects in one data structure, whilst making it simultaneously appear to be data structures appropriate to gameplay logic, graphical rendering, and AI planning. There is no database system that would efficiently support the manipulations typically performed by gameplay logic, graphical rendering, and AI planning, and the typical facilities of a database system -- persistence and caching, ad-hoc querying, transactions -- would represent needless overhead.
Discussion continued at GameModelingDatabase