RosettaPattern

The RosettaPattern defines a standard way for CLforJava and Java to interact. We stumbled on the pattern during the design of the TypeSystem for CLforJava. The original impetus was the need to implement the Common Lisp type system using the mechanisms available in Java. The Common Lisp type hierarchy is actually a somewhat "tangled" directed acyclic graph (DAG). The addition of the CLOS feature increases the complexity of the type system. Since Java specifically forbids multiple inheritence by classes, there were two choices:

  1. Build our own type mechanism using classes to define type connections
  2. Use the Java Interface object to mirror the CL type system since interfaces can be multiply inherited

Since option 1 would be a complex undertaking, we opted to examine option 2: modelling the CL TypeSystem using combinations of Java interfaces.

Mapping to Java Interfaces

The first stage was nearly trivial. We defined a set of interfaces based on the subtype diagram in the TypeSystem. To name the interfaces, we just took the Lisp type name and capitalized it (the Lisp type names are all upper case) - T, Atom, Symbol, etc. Each interface extended its parent(s) interface. This proved satisactory including the ability to prohibit further subclassing the Lisp types that cannot be subclassed: Fixnum, Null, and Nil. These interfaces were made final.

From Interface to Lisp Type

The second stage was to create a standard procedure for determining the Lisp type name from one of the Java interfaces. Since all of the Lisp type names are symbols defined in the COMMON-LISP package, we could add a public static final Symbol member that holds a reference to the appropriate symbol. In all interfaces, this member would be named typeName, each interface hiding the definition in each superinterface. The static variable of course required initialization code, leading to the third stage.

Factory Pattern

In the third stage, we had to write code that created a new Symbol instance to initialize the static final typeName member. The first concept was to use a public constructor in the SymbolImpl class. But when we looked at the problem from the perspective of a Java programmer using the public API we would provide, we didn't want to be constrained to the specific implementation class. Our solution was to add an inner class (Factory) with a public static method called newInstance. This factory method for creating Symbols is therefore available in the public API, e.g. Symbol.Factory.newInstance("FOO"). The original implementation called for each type interface to call the Symbol factory method to obtain its type Symbol. From there it was a small leap of imagination to provide Factory inner classes in each of the Lisp types that can instantiate an object of that type. For example, there is a Symbol.Factory.newInstance() method, but there is no Atom.Factory inner class since it's not possible to directly create an object of Lisp type ATOM. The result is that we have a uniform way to determine the type of any CLforJava object and the corresponding Lisp symbol for that type, and a uniform method for creating instances of these Lisp objects.

Once we had created the Factory inner class for the Package interface, we also added static code to create and initialize the public static final members that hold the 4 fundamental packages: COMMON-LISP, COMMON-LISP-USER, SYSTEM, and KEYWORD. This enabled us to directly use the Package intern method to create all of the type symbols required. This also had the added value of separating creation of an uninterned symbol through the Symbol interface and interning a symbol through the Package interface.

Adding Method Signatures

The fourth stage was the one we expected - to add the interface methods appropriate to objects of that type. For example, an instance of the List (LIST) type has methods to get the CAR and CDR of each element.

Benefits

This pattern has many benefits beyond the specific needs it addressed.

  • Java programmers have a consistent API for accessing and creating instances of all Lisp objects.
  • A Java programmer can create a subclass of any Lisp type by extending the corresponding Java interface and following the RosettaPattern.
  • The compiler can create these patterns as needed when it encounters DEFSTRUCT or DEFCLASS macros.

References

HyperSpec CLtL
link 1 Link 2

-- JerryBoetje - 12 Jul 2003

Topic revision: r5 - 2009-02-11 - 03:19:37 - MeganLusher
 
Home
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback