SKerwin: November 2007 Archives

Cargo Cult Linguistics

| | Comments (3)

There’s a word missing from the English language.

I know this because I have a thought that English won’t allow me to express directly, a concept that clearly needs a handle so we can package it up and pass it around and maybe stick it in a book or paint it on the wall. But because the concept doesn’t have a name I can’t explain it except by pointing out lots of things that are like it but aren’t quite it.

The original Star Trek series had an episode entitled The Squire of Gothos; it featured an alien who considered himself an expert on Earth based on having observed it from a distance. His understanding of what he saw, however, was fatally flawed by the method of observation; in seeking to recreate he things he had seen, he created “fire without heat and food and drink without flavor”. His distant and alien perspective prevented him from understanding that the point of fire was heat, and that the point of fine food is taste.

After US forces withdrew from Micronesia after WWII, the natives set up mock airfields, dressed themselves as soldiers, and attempt to cause supply planes to land. These cargo cults managed to create impressive facsimiles of landing strips but never managed to land a plane; their technologically primitive perspective stopped them from recognizing that the planes caused the landing strips rather than the converse.

Other people have noticed this missing word and latched on to the cargo cult metaphor to explain it. Richard Feynman coined the term ‘Cargo Cult Science’ in his 1974 Caltech commencement address. The term ‘Cargo Cult Programming’ has also come up — I think the term originated with Eric Lippert in one of his many excellent blog posts. In any case the underlying meaning is the same — understanding the form of something while completely mistaking the function.

After the success of the iMac, Dell came out with a host of low-end PCs in partially-translucent cases. Same pattern — the success of the iMac isn’t a result of the translucent plastic, that’s just one particular form of a more meaningful underlying concept. Subsequent iMacs don’t look anything like the original and are made of radically different materials, but they still have the same underlying nature.

The LG Voyager is being touted as Verizon’s answer to the iPhone, but it’s the same story again. The essence of the iPhone isn’t in any particular input technology or user interface convention; sure those things are revolutionary on their own, but the they’re just incidental. They are expressions of the core nature of the item, not the definition of that nature.

What brings this up? Actually, it’s politics. Reading Democratic positions on national security always makes me wish this word existed, because they all understand at a formal level that they need to support national security to be elected while simultaneously failing to understand what national security really means. It’s not a strictly partisan failing, of course — Republicans on gay marriage manage the same intellectual failure quite frequently — but that’s just what brought it to mind.

I wish someone would discover this word — it would make communicating these ideas a hell of a lot easier — but at the same time I’m itching to hear a Republican candidate blast his opponent for ‘cargo cult security’.

It happens rarely but regularly that I want to define a function in an abstract base class that return an instance of the child class. Typically this comes up because I want the ability to chain method invocations; it's much easier to type Marklar.CreateNew( ... ).Save( ).SendTo( ... ) than it is to break that out over three or more lines and introduce a local variable (at least to me). It also shows up when you're doing any heavily generic programming, when you want a clone( ) method, and a host of other unusual-but-not-unheard of cases.

The obvious (and unsatisfying) way to do this is as follows:

 
class BaseMarklar {
public BaseMarklar Self( ) { return this; }
}

class BlueMarklar : BaseMarklar {
public void Frob( ) { ... }
}


The problem there is that if I have a reference to a BlueMarklar and invoke Self, the result of the call isn't a BlueMarklar, but a BaseMarklar. If I want to do something BlueMarklar-specific to the result (like Frob( ) it) I'll need to cast down the inheritance hierarchy, which as we all know is akin to crossing the streams except with fewer toasted marshmallows and more unforeseen crashing bugs.

There are a lot of things you can try to get around this, but none of them work quite right. Virtual functions don't work, because an override with a different method signature isn't an override. Leaving it out of the base class and instituting an external rule that subclasses must implement Self( ) doesn't work, because then you can't call it polymorphically and besides nobody's going to implement it anyway.

Eventually you'll try getting clever; If you're in C#, for instance, you'll wonder: what if Self were a generic method?


class BaseMarklar {
public T Self<T>( ) { return (T) this; }
}

class BlueMarklar : BaseMarklar {
public void Frob( ) { ... }
}

class RedMarklar : BaseMarklar {
public void Frizzle( ) { ... }
}


You'll realize pretty quickly that this won't compile because the cast from this to T is invalid, but in keeping with the long-standing Microsoft tradition of solving every problem with a where clause, this is not an insoluble problem:

class BaseMarklar {
public T Self<T>( ) where T:BaseMarklar { return (T) this; }
}

That kinda-sorta works; You'll need to manually specify the generic type for Self( ) every time you invoke it (because there are no parameters from which to infer), but if you can live with the admittedly minor annoyance of typing MyBlueMarklar.Self<BlueMarklar>( ).Frob( ) then you might say it's good enough.

Until someone else has to work with or support your code, of course. Because you've given them a loaded gun, which they're free to point and fire just by typing MyBlueMarklar.Self<RedMarklar>( ).Frizzle( ). Whoops! Because the calling code provides the return type of Self( ), it's free to do dumb things like cast to a sibling type.

So how do you prevent this? Well, here's a solution:


class BaseMarklar<T> where T:BaseMarklar<T> {
public T Self() { return (T)this; }
}

class BlueMarklar : BaseMarklar<BlueMarklar> {
public void Frob() { ... }
}

class RedMarklar : BaseMarklar<RedMarklar> {
public void Frizzle() { ... }
}


Weird, ain't it? The more I think about this, the more I'm amazed that it works (and it does work... or at least it passes all my unit tests). We're building classes that inherit from a generic class that's parameterized on the class we're creating.

I haven't had time to test it out, but I'm 99% sure this would be impossible with C++ templates -- because templates are essentially expanded like glorified macros during compilation and base classes have to be successfully compiled before compiling subclasses, you'd end up with a circular dependency problem. I can imagine this causing stack overflows on a whole class of code-analysis tools.

Pages