Why is ‘pure polymorphism’ preferable over using RTTI?

An interface describes what one needs to know in order to interact in a given situation in code. Once you extend the interface with “your entire type hierarchy”, your interface “surface area” becomes huge, which makes reasoning about it harder.

As an example, your “poke adjacent oranges” means that I, as a 3rd party, cannot emulate being an orange! You privately declared an orange type, then use RTTI to make your code behave special when interacting with that type. If I want to “be orange”, I must be within your private garden.

Now everyone who couples with “orangeness” couples with your entire orange type, and implicitly with your entire private garden, instead of with a defined interface.

While at first glance this looks like a great way to extend the limited interface without having to change all clients (adding am_I_orange), what tends to happen instead is it ossifies the code base, and prevents further extension. The special orangeness becomes inherent to the functioning of the system, and prevents you from creating a “tangerine” replacement for orange that is implemented differently and maybe removes a dependency or solves some other problem elegantly.

This does mean your interface has to be sufficient to solve your problem. From that perspective, why do you need to only poke oranges, and if so why was orangeness unavailable in the interface? If you need some fuzzy set of tags that can be added ad-hoc, you could add that to your type:

class node_base {
  public:
    bool has_tag(tag_name);

This provides a similar massive broadening of your interface from narrowly specified to broad tag-based. Except instead of doing it through RTTI and implementation details (aka, “how are you implemented? With the orange type? Ok you pass.”), it does so with something easily emulated through a completely different implementation.

This can even be extended to dynamic methods, if you need that. “Do you support being Foo’d with arguments Baz, Tom and Alice? Ok, Fooing you.” In a big sense, this is less intrusive than a dynamic cast to get at the fact the other object is a type you know.

Now tangerine objects can have the orange tag and play along, while being implementation-decoupled.

It can still lead to a huge mess, but it is at least a mess of messages and data, not implementation hierarchies.

Abstraction is a game of decoupling and hiding irrelevancies. It makes code easier to reason about locally. RTTI is boring a hole straight through the abstraction into implementation details. This can make solving a problem easier, but it has the cost of locking you into one specific implementation really easily.

Leave a Comment