Generic programming is a paradigm supported by surprisingly few languages. Among those are C++, D, and Haskell. GP usually uses static polymorphism techniques (templates in C++), where hierarchies are created with concepts (implicitly defined through usage and documentation in C++, type classes in Haskell) instead of object-oriented interfaces. Concepts differ from interfaces in some important aspects. One is that a concept is not a type; you can't have a variable of a concept's type (or reference/pointer to it). Another is that a concept can mandate more than your average OO interface. An interface contains functions. A concept can require functions, static functions, and so-called "associated types", i.e. types that are related to the concept. (I'll give you an example in a moment.) Moreover, concepts aren't necessarily unary; a concept can be binary, trinary, or n-ary. For example, Comparable could be an unary concept (two objects of type T that models Comparable can be compared), but it could also be a binary concept (an object of type T can be compared with an object of type U if T and U together model Comparable).
Take, for example, a simplified version of your math library, which has only two types, Integer and Real, plus a unifying concept/interface, Number.
In classic OOP, you would have something like this (I'll use Java syntax here):
Code:
interface Number {
abstract Number complement();
abstract Number absolute();
abstract Number add(Number n); // ???
abstract Number zero(); // ???
}
We already hit the limitations of OO in most current languages here. How do you properly implement add(Number)? You need multiple dispatch to account for the two parameters. If you look at the Wikipedia article for MD, you'll find that you probably don't know most of the languages that actually support it. (Prominent members are Common Lisp, Perl 6 and C# 4.0. Incidentally, only one of those is older than a year.)
What about zero(), i.e. a way of obtaining the number's zero-element? That ought to be a static function, but what languages allow virtual static functions? The very idea is a contradiction in terms.
Here's another issue: let's do this thing in C++:
Code:
struct Number {
virtual ptr<Number> complement() = 0;
};
What is ptr? Is it a raw pointer? If so, who is responsible for freeing the memory?
OK, enough of that. Let's look at the generic version of this. Remember that I said that concepts are specified in documentation? (C++0x was supposed to have a programmatic way of specifying concepts. But that was not ready and therefore was pulled.)
Given a type T that models Number, and instances t, t1 and t2 of this type, the following expressions shall be valid:
Expression: numeric_traits<T>::zero()
Type: T
Semantics: the additive identity element of T
Expression: t1 + t2
Type: T
Semantics: an associative, commutative binary operation with the identity element numeric_traits<T>::zero()
Expression: -t
Type: T
Semantics: t + -t == numeric_traits<T>::zero(), that is, -t yields the complement of t in regards to the + operation
There are some interesting aspects to this. For example, we have restricted addition to adding two objects of the same type, i.e. Real + Integer is not allowed by this concept. But we could add a binary concept Addable that requires its two types to be addable, and have Real and Integer model that. In fact, it is generally better to define a number of smaller concepts and have the big concepts tie them together. It gives us greater flexibility in defining the requirements of functions operating on these objects. For example, the above Number concept is already too restrictive: natural numbers cannot provide unary minus, and depending on your definition do not have zero() either.
An interesting ability of concepts is retroactive modelling. You might observe that the built-in type int already fulfils the requirements of the Number concept, except for that zero() function. But we can add that in after the fact.
Code:
template <typename T> struct numeric_traits; // basic declaration, specialize it!
template <> struct numeric_traits<int> { int zero() { return 0; } };
With inheritance-based object-oriented design, you can't retroactively model. If you want to tie that third-party bignum class into your design, you'll have to write a wrapper class that derives from your interfaces and implements the operations. This leads to runtime cost, as you have to wrap and unwrap these objects, as well as complexity cost, because you have to keep track of the lifetime of the wrappers and their inner objects.
With concepts, adapting the bignum class to the concept would probably come down to implementing a few free functions, which the compiler can probably inline and optimize away.
Also, did you notice that the concept version of the code dealt with objects by value, and not by reference? In C++, that's a property you usually want.