I just read a post by Bruce Eckel entitled Generics Aren't where he writes

My experience with "parameterized types" comes from C++, which was based on ADA's generics... In those languages, when you use a type parameter, that parameter takes on a latent type: one that is implied by how it is used, but never explicitly specified.

In C++ you can do the equivalent:

class Dog {
public:
  void talk() { }
  void reproduce() { }
};

class Robot {
public:
  void talk() { }
  void oilChange() { }
};

template<class T> void speak(T speaker) {
  speaker.talk();
}

int main() {
  Dog d;
  Robot r;
  speak(d);
  speak(r);
}

Again, speak() doesn't care about the type of its argument. But it still makes sure – at compile time – that it can actually send those messages. But in Java (and apparently C#), you can't seem to say "any type." The following won't compile with JDK 1.5 (note you must invoke the compiler with the source -"1.5" flag to compile Java Generics):

public class Communicate  {
  public <T> void speak(T speaker) {
    speaker.talk();
  }
}

However, this will:

public class Communicate  {
  public <T> void speak(T speaker) {
    speaker.toString(); // Object methods work!
  }
}

Java Generics use "erasure," which drops everything back to Object if you try to say "any type." So when I say <T>, it doesn't really mean "anything" like C++/ADA/Python etc. does, it means "Object." Apparently the "correct Java Generic" way of doing this is to define an interface with the speak method in it, and specify that interface as a constraint. this compiles:

interface Speaks { void speak(); }

public class Communicate  {
  public <T extends Speaks> void speak(T speaker) {
    speaker.speak();
  }
}

What this says is that "T must be a subclass or implementation of Speaks." So my reaction is "If I have to specify a subclass, why not just use the normal extension mechanism and avoid the extra clutter and confusion?"

You want to call it "generics," fine, implement something that looks like C++ or Ada, that actually produces a latent typing mechanism like they do. But don't implement something whose sole purpose is to solve the casting problem in containers, and then insist that on calling it "Generics."

Although Bruce didn't confirm whether the above limitation exist in the C# implementation of generics he is right that they have the same limitations as Java generics. The article An Introduction to C# Generics  on MSDN describes the same limitations Bruce encountered with Java generics and shows how to work around them using constraints as Bruce discovered. If you read the problem statement described in the article on MSDN it seems the main goal of C# generics is to solve the casting problem in containers.

What I find interesting about Bruce's post is the implication that to properly implement generics one most provide duck typing. I've always thought the behavior of templates in C++ was weird in that one could pass in a parameter and not enforce constraints on the behavior of the type. Yet it isn't really dynamic or latent typing because there is compile time checking to see if the type supports those methods or operations.

A few years ago I wrote an article entitled C++ in 2005: Can It Become A Java Beater?  in which I gave some opinions on an interview with Bjarne Stroustrup where he discussed various language features he'd like to see in C++. One of those features was constraints on template arguments, below is an excerpt from my article on this topic

Constraints for template arguments

Bjarne: This can be simply, generally, and elegantly expressed in C++ as is.

Templates are a C++ language facility that enable generic programming via parameteric polymorphism. The principal idea behind generic programming is that many functions and procedures can be abstracted away from the particular data structures on which they operate and thus can operate on any type.

In practice, the fact that templates can work on any type of object can lead to unforeseen and hard to detect errors in a program. It turns out that although most people like the fact that template functions can work on many types without the data having to be related via inhertance (unlike Java), there is a clamor for a way to specialize these functions so that they only accept or deny a certain range of types.

The most common practice for constraining template arguments is to have a constraints() function that tries to assign an object of the template argument class to a specified base class's pointer. If the compilation fails then the template argument did not meet the requirements. 

The point I'm trying to get at is that both C++ users and its inventor felt that the being able to constrain the operations you could perform on the parameterized type as opposed to relying on duck typing was a desirable feature.

The next thing I want to point out is that Bruce does mention that generic programming in C++ was based on Ada's generics so I decided to spend some time reading up on them to see if they also supported duck typing. I read Chapter 12 of the book Ada 95: The Craft of Object-Oriented Programming where we learn

In the case of a linked list package, we want a linked list of any type. Linked lists of arrays, records, integers or any other type should be equally possible. The way to do this is to specify the item type in the package declaration as private, like this:

    generic
        type Item_Type is private;
    package JE.Lists is
        ...
    end JE.Lists;

The only operations that will be allowed in the package body are those appropriate to private types, namely assignment (:=) and testing for equality and inequality (= and /=). When the package is instantiated, any type that meets these requirements can be supplied as the actual parameter. This includes records, arrays, integers and so on; the only types excluded are limited types...

As you can see, the way you declare your generic type parameters puts restrictions on what operations you can perform on the type inside the package as well as what types you can supply as parameters. Specifying the parameter as ‘range <>’ allows the package to use all the standard operations on integer types but restricts you to supplying an integer type when you instantiate the package. Specifying the parameter as ‘private’ gives you greater freedom when you instantiate the package but reduces the range of operations that you can use inside the package itself.

So it looks like Ada gave you two options, neither of which look like what you can do in C++. You could either pass in any type then the only operations allowed on the type were equality and assignment or you could pass in a constrained type. Thus it doesn't look like Ada generics had the weird mix of static and duck typing that C++ templates have.

I am as disappointed as Bruce that neither C# nor Java support dynamic typing like languages such as Python or Smalltalk but I don't think parametric polymorphism via generics has ever been used to solve this problem. As I have pointed out neither Ada nor C++ actually give him the functionality he wants so I wouldn't be too hard on Java or C# if I was in his shoes.


 

Thursday, 11 March 2004 20:06:47 (GMT Standard Time, UTC+00:00)
It's been a little while (about three years) since I last used Ada, but I did do some work in generics. You can extend the constraints by using a 'with' declaration (bear in mind that Ada isn't a true object-oriented language; all methods are declared at package scope or nested within another method, they are not declared lexically as part of a type).

All constraints must be met, in Ada, to be able to instantiate a package. It isn't nearly as relaxed as C++, where you only need meet the constraints of the features you actually use ([contrived example] if you never call sort, you don't need to provide an operator<).

More horrible detail at http://www.adahome.com/rm95/rm9x-12.html.

The major problem with the C++ and Ada schemes is that you need the source (or a pre-parsed, but not really compiled, version) of the library. This isn't acceptable in the C#/Java world. Alternatively, you could fall back on Reflection or use Object directly (in C#).
Thursday, 11 March 2004 20:56:25 (GMT Standard Time, UTC+00:00)
There's a difference between allowing "duck typing" and allowing only "duck typing". If I understand it correctly, Stroustrup is not saying that he wants coders to but forced to put constraints on templates. Constraints are a proposed additional feature, not different way of making an old feature work. In that sense, I don't think the fact that Stroustrup's exposal of constraints is a good defense of not allowing "duck typing" in Java or C#.

Though I guess I'm hard pressed to find a situation where a common base class would be a problem.
Thursday, 11 March 2004 22:23:59 (GMT Standard Time, UTC+00:00)
Bruce's choice of examples is pretty poor. If a method only needs to assert the existence of a single named method on an object it is passed, then of course you don't need generics. Just do a:

void speak( Speaks speaker ) { ... }

instead. Of course, this only works with methods--classes have no recourse without generics, unless you want to play with Objects and casting.

With generics on the other hand you can do more complicated things, e.g.:

public &lsaquo;T extends Map&lsaquo;?,?&rsaquo; & Cloneable&lsaquo;T&rsaquo; &rsaquo; void playWithCloneableMap( T playee );

which could not be accomplished without generics, with or without "duck typing." (Note: code fragment not tested with a compiler, but then the syntax isn't the point)

I really wish people would stop blaming Java for not being Python or Ruby (or even C++, though that one is much rarer.)
Vulcannis
Thursday, 11 March 2004 22:28:09 (GMT Standard Time, UTC+00:00)
Drat, that didn't come out very well. I tried it with angle brackets but I got an error saving the reply. I must be missing the obvious link to permissable reply content. :(
Vulcannis
Comments are closed.