modified to C
0
(perhaps to add a method m), C
0
objects cannot be serialized and sent to
a distributed process with the original definition C, even if C
0
is a strict extension of C.
As an example of the reuse benefits of structural subtyping, suppose class A has
methods foo(), a() and b(), and class B has methods foo(), x() and y(). Suppose
also that the code for A and B cannot be modified. In a language with structural sub-
typing, A and B share an implicit common interface { foo } and a programmer can
write code against this interface. But, in a language with nominal subtyping, since the
programmer did not plan ahead and create an IFoo interface and make A and B its
subtypes, there is no way to write code that takes advantage of this commonality (short
of using reflection). In contrast, with structural subtyping, if a class C is later added that
contains method foo(), it too will share this implicit interface. If a programmer adds
new methods to A, A’s interface type will change automatically, without the program-
mer having to maintain the interface himself. If B or C also contain these new methods,
the implicit combined interfaces will automatically exist.
Nominal subtyping also has its advantages. First, it allows the programmer to ex-
press and enforce design intent explicitly. A programmer’s defined subtyping hierar-
chy serves as checked documentation that specifies how the various parts of a pro-
gram are intended to work together. Second, explicit specification has the advantage
of preventing “accidental” subtyping relationships, such as the standard example of
cowboy.draw() and circle.draw(). Nominal subtyping also allows recursive types
to be easily and transparently defined, since recursion can simply go through the de-
clared names. Third, error messages are usually much more comprehensible, since, for
the most part, every type in a type error is one that the programmer has defined explic-
itly. Finally, nominal subtyping enables efficient implementation of external dispatch.
External dispatch is provided by number of statically typed languages, such as Cecil
[6, 7], MultiJava [8], among others. External methods increase the flexibility and evolv-
ability of code because they do not fix in advance the set of methods of a class. Consider
the example of a class hierarchy that represents AST nodes. (This motivating example is
expanded further in Sec. 2.3.) Suppose this is part of a larger system, which includes an
IDE for editing elements represented by this AST. Now suppose a programmer wishes
to add new functionality to the IDE but cannot modify the original source code for the
AST nodes. The new function provides the capability to jump from one node to a node
that it references; this differs depending on what type of node is selected. Clearly, this
functionality cannot be written without code that somehow performs dispatch on the
AST class hierarchy.
In a language without external dispatch, the developer has limited choices. Usually,
she must resort to manually writing instanceof tests, which is tedious and error-
prone. In particular, if a new element is added to the AST hierarchy, the implementation
will not behave correctly.
If the developers of the original class hierarchy anticipated this need and imple-
mented the Visitor design pattern, it would then be easy to add new operations to the
hierarchy, but then it would be difficult to add new classes. At best, Visitor trades one
problem for another.
On the other hand, in a language with external dispatch, a programmer simply writes
an external method that dispatches on the AST class hierarchy (i.e., separate from its