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.)

Modern Perl: The Book

cover image for Modern Perl: the book

The best Perl Programmers read Modern Perl: The Book.

sponsored by the How to Make a Smoothie guide



About this Entry

This page contains a single entry by chromatic published on May 4, 2011 10:42 AM.

Reuse is Merely a Happy Accident was the previous entry in this blog.

Pains of the Past, Begone! is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Powered by the Perl programming language

what is programming?