When You Lack Cheap and Easy Polymorphism

The quality and cost of your language's abstractions can affect the
applicability of those abstractions. Consider Parrot and its intrinsic data structure, the
PMC. The design of PMCs resembles Perl 5 SVs in
a philosophical sense, if not a direct working relationship. The guiding design
principle is "A PMC should be able to support a basic set of core operations
common to all core PMCs".

Parrot's core PMCs have dozens of these operations, such as getting and
setting integer, numeric, and string values. They support a clone operation, an
invocation operation, an instantiation operation, and more. While some of these
operations make little sense (invoking an Integer PMC throws an exception for
obvious reasons), every PMC has to support them sufficiently that attempting to
perform that operation should not cause the VM to crash.

Parrot's core PMCs are big wads of C code. All of these operations in
Parrot's core PMCs are C functions gathered in a large table of function
pointers. Every PMC type has one of these tables. Adding a new core operation
to Parrot means adding a new function to every PMC table. Every new function
added to the PMC table thus adds to the memory and startup costs of Parrot
overall. Similarly, adding a new PMC type—even a specialization of an
existing type—increases the memory and startup costs of Parrot overall
because a new type needs an entirely new and distinct function pointer
table.

These are implementation details quite solveable with some clever
programming, but the most interesting point in my mind is how these
implementation details have influenced the development of Parrot and Parrot
languages. In particular, there's a subtle (and likely correct) belief in the
project that adding a new PMC is an expensive operation in terms of Parrot's
footprint and the management of its source code.

That's true to some extent, which is a shame, because Parrot's suitability
as a VM for interoperable languages comes in no small part from the flexibility
of the PMC system.

The cost of the belief in the heavy cost of new PMCs is that the core of
Parrot takes advantage of far less polymorphism than it should. Consider the
difference between a named function, an anonymous function, a method, a
closure, a continuation, an exception handler, a binding to a shared library
function, and a lazy thunk (perhaps a generator). You can invoke all of those
items. You can pass in zero or more arguments. They can each return zero or
more values. Yet they each perform very different mechanisms of control
flow.

An ideal Parrot design would have separate PMC types for each logical type.
A boring old named function which takes no parameters could avoid parameter
passing code altogether, while the binding to a shared library function likely
needs to marshall data to and from the appropriate ABI. A generator should be
fast and lightweight, while an exception handler may have to manipulate control
flow in interesting ways to resume an exception or at least resume at the
appropriate place in control flow based on which exceptions it can handle and
which it must decline.

Yet Parrot doesn't have multiple specialized PMC types representing all (or even most) of the necessary variants of invokable PMCs. It only has a handful. Those precious few would benefit from the judicial and pervasive application of the pattern Replace Conditional With Polymorphism.

Unfortunately, PMCs are far more expensive than they should be, and so it's
cheaper to make the existing PMCs far more complex (and subsequently far more
expensive on the individual level, not only the type level) than they should
be. If PMC types were cheap, individual PMCs could be smaller and
smarter and the entire system itself could have much stronger pressure toward
simplicity.

Then again, writing an object system in C using native C data structures and
dispatch mechanisms and language patterns governs what you can and can't
accomplish cheaply and easily. One of the goals of the Parrot Lorito project is
to reduce the tight coupling of Parrot's internals with C so as to make
necessary changes in Parrot sufficiently inexpensive.

(Any metaobject system in, for example, Perl 5 would do well to consider these lessons.)