August 4, 2009

The Different Faces of Polymorphism

Others might think polymorphism is the skill used by Odo from Deep Space Nine when he shifts his form. But we computer geeks now it better. Polymorphism is the one stop show that makes writing software so much fun. Or to put in another way, it provides us with the power of generalization, loosely coupling and ultimately maintainability. But polymorphism does not just come in the shape of the too well known interfaces with its precious virtual methods. Different paradigms provide us with different mechanisms to wield the Sword of the Many Forms. In this article we will give an introduction to several of these incarnations. Namely C++'s compile time polymorphism and that what they call Duck Typing in Dynamic Typed Languages.

Let's start with the basic question: What the hell is polymorphism? It is the ability to specify behavioral contracts on the one hand and to fulfill them on the other. You can say what you expect from someone by defining an contract. He in turn could realize your contract however he wants, as long as he sticks to your defined expectations. With this simple mechanism we basically created the founding of what makes software development possible in the large scope.

Let's start simple.

Runtime Polymorphism

Yeah, we are familiar with this one. It's our daily work. Let us just exercise through it. Here is our contract:
    public interface Phone {
        void call(String phoneNumber);
    }
We simply expect a Phone. If someone provides us something fulfilling the Phone's demands, we can make a call:
    public final class Client {
        void makePhoneCall(Phone phone){
            phone.call("555-6832");
        }
    }
Then there is a very simple Phone implementation to complete the example
    public final class ToyPhone implements Phone {
        public void call(String phoneNumber){
            System.out.println("Ring Ring");
        }
    }
And finally the full scenario breaks down to:
    Phone phone = new ToyPhone();
    Client client = new Client();
    client.makePhoneCall(phone);
All in all not sophisticated, but just all we need to explain the general idea. You see: With the interface we define our contract. Our Client relies on that we give him something like a Phone, then he will just use it. How does it work? This is due to the way Java implements public methods. Actually they are virtual methods, which means, somewhere under the hood there is a vtable, which maps a class' method to a memory address where the implementation of that method could be found. This is all static runtime information. From an execution flow's point of view calling a method means getting the right address from the vtable and then jump there and continue (if we leave out compiler optimization...). Static means, that the compiler already knows where to look into the vtable, but it will contain the proper value only at runtime, of course. This is no magic at all...

Compile Time Polymorphism

Ah ha. Now it is getting more exciting. Let's play the same game, just with C++ meta programming:
    template<typename PHONE> class Client {
    public: 
     void makePhoneCall(PHONE& phone){
      phone.call("555-3223");
     }
    };

    class MobilePhone {
    public: 
     void call(const std::string& phoneNumber);
    };

    void MobilePhone::call(const std::string& phoneNumber){
     std::cout << "Hello?" << std::endl;
    }

    int main(){
     Client<MobilePhone> client;
     MobilePhone phone;
     client.makePhoneCall(phone);
    }

Let's see what we've got here. We see the client, yes that's fine. Ok, this is a templatized class, maybe we would not have expected it, but we just accept it for now. What else? Yes, the implementation of our Phone. This is a C++ class. But, wait: call() is NOT virtual, what the heck... hm let's wait a further second and finish the example. Ok, main() will run our example. And it does what we expect: Create an instance of the client, create an instance of the phone and then let the client do his call. But something is still odd... Yes! Where is the so called contract? It's not there!? And this compiles? Obviously yes... Hm... Now we owe some explanations!

What we see here is called meta programming used by the modern C++ guys and has been first described by Andrei Alexandrescu in Modern C++ Design. He explains how to utilize C++ template compiler for writing programs rather than the actual C++ compiler. Of course this blog post cannot satisfy all which has to be said about meta programming, but just so much: In our particular case we just imply an interface. You and I know, there is nothing which defines our contract explicitly. Psst! But the template compiler does not know that. It just knows that it creates a template instance for the type MobilePhone, and that PHONE and therefore MobilePhone is called on the method call(). While compiling the instantiated code the C++ compiler in turn does its usual job. MobilePhone has such method, hence everything works out fine.

I've heard people argue "What's that nonsense! Templates are for generalizing on types! Like the STL does it!" But think about it: Yes templates are used for generalization. For sure. But in this case we don't generalize on types, we generalize on behaviour. And it turns out more than fine. Are there any benefits? Yes! We've already mentioned the most crucial one: There are no vcalls involved. No vcalls means, the C++ compiler could apply plenty of its optimization strategies, which results in aweful cool runtime behaviour. Eager to see more? Boost and CGAL are awesome examples of modern C++ design.

Actually the C++ guys find this concept of compile time polymorphism that attractive, that they try to incorporate it into a future C++ standard under the term Concepts. Although they've currently postponed it from C++0x.

Duck Typing

Finally we come to our Dynamic Typed Languages. Here they say, if it talks like a duck and walks like a duck then it is a duck. What does this mean? Have a look.

The important aspect here is, besides others, that an instance is not checked for its type during compile time as it is done with Java or C++ for example, but only on runtime, just at the moment a method is called on that instance. For me as a Java Enthusiast it's obvious to give an example in Groovy:

    class Client {
        def makePhoneCall(def phone){
            phone.call('555-3126')
        }
    }
    
    class PhoneBooth {
        def call(def phoneNumber){
            println('Beep...')
        }
    }
    
    static def main(){
        def client = new Client()
        def phone = new PhoneBooth()
        client.makePhoneCall(phone)
    }

It's nearly the same game as with C++ meta programming. We have no explicit definition of our contract. This is just stated implicitly. But since method dispatching is done just when we execute call() on phone, and it's instance PhoneBooth owns such a method, everything works out fine again. But why is it helpful? Because it yields efficient code. Of course, yes, there are a lot of reasons why it is helpful to have a proper static type system. I will not deny it. I also favor it of course. But there are a certain amount of valid use cases, which have other desires, like implementation efficiency. Consider a unit test for example. Maybe you have decoupled everything loosely enough. Your class under test just knows other interfaces. But you still have to create stubs or mocks for those. This is acceptable of course (at least better than to avoid it), but even then it is boiler plate. It does not support the readability of your test. On the other hand, you can easily craft a stub for your interface out of maps and closures. Very dense code, very readable.

Finally

What have we learned today? Polymorphism is cool. And besides this there are different paradigms having different styles of polymorphism. Some have explicitly defined contracts, others don't. But they all obey to the same principle. A contract defines expectations. Implementations fulfill this contract.

No comments: