29 April 2006

Templates as type-checked dynamic typing

In C++, templates provide compile-time polymorphism (similar to parametric polymorphism) by expanding templates into different type-checked functions and types during compilation. std::vector<MyObject> is a unique vector<> type because it implements the required interface, and the determination of the "derived" vector<MyObject> type occurs at compile-time. Circle is a unique Shape type for the same reason, but the determination of derived type (given a Shape reference) occurs at run-time.

Using MyObject within vector<> means that you've entered into a contract stating that MyObject will provide those features that vector<> expects. This contract is different from polymorphism using inheritance because the is-a relationship does not exist. Types used within templates do not need to exist in a specific class hierarchy. However, those types must contain an implied interface--one which the template expects. This implied interface is managed similar to how dynamically typed languages manage accessing interfaces on class instances. A dynamically typed language only determines if FunctionA() exists on MyObject at run-time and at the point of the call. With templates, FunctionA() is only required at the point of the call, but checking occurs when the code is compiled. In both dynamically typed languages and statically typed templates, an object can be missing the required method if the code never gets called.

Using the following class (in pseudo-code):

class MyClass
{

    function set(String param1) {do something;}

}

In a dynamically typed language, a specific interface is validated only if the code is called:

function FunctionA(param1)
{

    param1.set("a string");

}

function FunctionB(param1)
{

    // Run-time error here, only if MyClass passed in.
    param1.set(23);

}

MyClass myClass;
FunctionA(myClass);

With run-time polymorphism, a specific interface is required but accessed only when the type is used:

class AnotherClass extends MyClass
{
}

function FunctionA(MyClass & param1)
{

    param1.set("a string");

}

function FunctionB(MyClass & param1)
{

    // Compile-time error here, even though it is never called.
    param1.set(23);

}

AnotherClass anotherClass;
FunctionA(anotherClass);

With compile-time polymorphism, a specific interface is validated only the the type is used:

template MyTemplate<type MYTYPE>
{

    function FunctionA(MYTYPE param1)
    {

        param1.set("a string");

    }

    function FunctionB(MYTYPE param1)
    {

        // Compile-time error here, only if this method is referenced.
        param1.set(23);

    }

}

MyTemplate<MyClass> myTemplate;
MyClass myClass;
myTemplate.FunctionA(myClass);

This gives templates the flavor of dynamic typing (classes need not be of a specific type) but with the benefits of static type checking.

[ posted by sstrader on 29 April 2006 at 1:33:54 PM in Programming ]