From my (dynamically typed) point of view both templates and generics are a workaround to solve a problem treacherously introduced by static typing (which is static typing itself). That is to say: loss of generality. Moreover, while some type systems are quite well done (ML), others are really a PITA. I especially hated Java 1.x (with x < 5; see here) for its claims on type-safety and proudly sported static typing, while you had to manually cast back and forth from collections. Back then I quite compared the language with C++ which, with all its defects, at least allowed us to have typed collections. Besides, I am quite fond of template metaprogramming (especially when used for efficiency) and I have an insane passion for the boost library.
Quite unfortunately using simple lambda and bind in C++ increases compilation times from an unspecified amount of time. Basically I resorted to use lambda only in implementation files. Moreover, I found out that most C++ programmers simply find some usages of the templates too hard to understand and that seriously hinders collaboration.
On the other hand, I rather hated the way dynamic polymorphism (inheritance) interacts with static polymorphism (templates). In fact containers “copy” objects inside (which is good, defensive and whatever). However, this means that if you “cannot” put an object of type B inside a container of A’s, if B is a subtype of A. The problem is that you cannot create containers of A& (because stuff you put in most containers can be moved around). Essentially, you have to use pointers or better smart pointers, but that is a pita nonetheless.
Even more tedious is the way inheritance relationships work with templates. Consider the following example:
#include <vector> class A { }; class B : public A { }; void f(std::vector<A *> const& v) { // do... } int main() { std::vector<B *> vb; f(vb); }
Given the C++ object model the example must not work, since the fact that B is a subtype of A does not imply that T<B> is a subtype of T<A>. In fact, if it were the case, then Liskov Substitution Principle would be violated; and that would be very bad. Consider this: suppose that B subtype A => T<B> subtype T<A>. Then for the Liskov substitution principle we could use an object of type T<B> wherever we can use an object of type T<A>. However, this is not the case as it’s perfectly reasonable to add an object of type A to a collection of A’s; but it would be plainly wrong to add it to a collection of B’s. By the way, in Java we have somewhat the same problem, though it is mitigated by the use of bounded wildcars.
No comments:
Post a Comment