or "Why type erasure is not such a bad thing" or "Why generics in C# are not that good"
Last Friday, I read an article that has been on my to do list way too long: Adding Wildcards to the Java Programming Language. I've seen wildcards in Java; I've used wildcards in Java; and I've even read the Java Language Specification on wildcards, but did yet not get the essence of wildcards.
This paper makes the need for wildcards very clear and explains why the work on wildcards and parameterized types is novel. Unfortunately, generics are often ridiculed by functional programmers. They claim that their type systems have been more expressive for decades. Fortunately, this paper clearly explains the issues of introducing generics in an object oriented setting.
The problem with basic parameterized types is subtyping. For
example, although Integer
is a subclass of
Number
, a List<Integer>
is not a
subtype of a List<Number>
. Hence, if a method
requires a List<Number>
as an argument, then you
cannot pass a list of List<Integer>
to it.
Why is a List<Integer>
not a subclass of
List<Number>
? Well, this is related to
covariance and contravariance. A type declaration is covariant
if it allowed to be more specific. For example, return types are
covariant. A method that is declared to return a
Number
can be overridden to be more
specific and return an Integer
. On the other
hand, parameter types are contravariant: a method that
is declared to accept a Integer
argument can be
implemented in a more general way by allowing all
Number
s.
The problem with type parameters is that they are used in method
parameters as well as return types. Hence, they are restricted
to the intersection of covariance and contravariance:
invariance. Thus, type parameters are invariant and a
List<Integer>
is not a subclass of
List<Number>
.
If Java would be restricted to basic parameterized types and
methods, then it is quite difficult to come up with a good
signature for a method that works on a List
that
contains any Number
. In fact, you cannot even
declare the type of lists with abitrary numbers! Allowing
arbitrary numbers a generic method with a dummy type parameter
for the 'real' Number, i.e.
<T> void doSomething(List<T extends Number>) { ... }
This works, but it gets quite mind-boggling if the types get
more complex ("The more interesting your types get, the less
fun it is to write them down!" -- Benjamin C. Pierce).
These types are not only hard to write down: it
does not even work in all cases. For example, you cannot declare
a field that contains arbitrary Number
s, since you
cannot introduce a dummy type variable for a field.
Wildcards are a language feature that make it a bit more fun to
write down these types. Actually, it is a language feature that
is necessary to write down these types, since the dummy
type variable is just a workaround and uses the type of the
method to declare the type of the argument. The field example
shows that you cannot write down the type itself. Wildcards are
based on the notion of use-site variance. Using
wildcards, you can declare that your list is covariant:
List<? extends Number>
or contravariant:
List<? super Number>
. For more details, read the
paper!
Unfortunately, C# will not support wildcards or a similar mechanism. The implementation strategy does not allow the introduction of wildcards (generics are implemented in the runtime instead of by type erasure). This is a bit surprising, since the implementation strategy is often claimed to be superior. What disappoints me is that the designers of C# are not willing to admit that subtyping is an issue and that wildcards are a solution. See the weblog of Eric Gunnerson: Puzzling through Erasure II and the section on wildcards in JavaOne: Day One.
3 comments:
Are all this stuff really usable?
For me it looks like object-oriented strictly typed languages (C++ clones lol) constantly get more and more complex syntax.
C# described in a 3-, may be 5-, year old book had much simpler syntax then C++. Now they are at least comparable.
Java going this way too. Looks like since 1.1.8 it got templates.
Personaly, i switched to agile languages few years ago. I like languages described in a few sentences.
Erlang pretty close to be good for me.
Lisp isn't an option because it's really hard behind it's illusory simplicity with all that functions to be used and untamed macros. Python and Perl all got too many methods/operators to remember. Ruby looks like Python clone with cool features for me.
I doesn't want to start flame war, i just want to get wider understanding of things. May be i missed something really important and going wrong way?
Yes Ivan, this stuff is important.
Just a quick example, a while ago I implemeted a small GA framework for Java, using generics, and recently tried to port it to C#, but couldnt implement it as generic as I could in Java.
A snippet of Java code:
interface AlleleValue< T > {
T minValue();
T maxValue();
// Can have different bounds for same type //parameter T
T value();
}
interface Chromosome< T extends AlleleValue< ? > > {
T[] genes;
}
- So I know I can check upper and lower bounds on the Allele (Gene values) in each chromosome easily.
However, this is imposible in C#.
The only way is to have 2 type parameters for Chromosome:
interface Chromosome< T,V > where T : AlleleValue< V > <-- Here I would much prefer a wildcard!
In the Java version I can have a chromosome with some Integer genes, and some String genes mixed, but in the c# version only a single value type must be used for all alleles. Of course I ended up removing the type parameter from the AlleleValue interface having each method return object instead, so the code using the api is full of (potentially bad) class casts.... UGLY!
Addendum:
Ivan, Java's generics are a completely different beast to C++'s templates.
Which leads to far less complications in most cases imho...
People seem fond of saying generics are making things too complicated, but in my experience, they result in much cleaner (and safer) code...
Post a Comment