.NET doesn't get generics right, having type erasure is among the best features of Java.
The actual reason for why you feel the need for reification is because the type systems of both C# and Java are so poor that you end up in situations where you really need to do "isInstanceOf" checks and castings. But people are missing the forest from the trees here, because these are essentially holes in the type-system, only needed because the languages are not expressive enough. Because really, you don't really need to check whether something is an instance of "List<int>" at runtime, in a static language, unless your language sucks ;-)
As to why type erasure is among the best features of Java, that's because with reification it gets hard to support other languages. It's kind of ironic, but .NET CLR was once marketed as the multi-language runtime, but time has been on the JVM's side in that regard.
For example F# doesn't support higher-kinded types because the CLR doesn't support higher-kinded types and introducing support for HKT would mean for the F# compiler to basically do type erasure by itself, which would place it at a serious disadvantage versus the host language in terms of performance. Scala supports higher-kinded types, which means you can express in Scala types such as the Monad and the Applicative, which are essential for FP in static languages, without sacrificing performance or Java interoperability. Even OCaml supports higher-kinded types.
And this isn't just about static languages. Pick any dynamic language you can think of, take your pick from Ruby, Python, Clojure and Javascript and compare the performance of the implementations on the JVM versus the CLR. OK, it's sort of an unfair comparison, given the JVM implementations have had more resources invested in them, but you know, one of the reasons for why dynamic languages work better on top of the JVM is because the JVM's bytecode is basically dynamically typed, with the last barriers being brought down along with invokedynamic. But by introducing generics reification on the other hand you end up with more static info in the bytecode that dynamic languages have to work around.
The only clear advantage to reification is the specialization for primitives that the CLR is doing for things like the standard collections. But you know, this can be a job for the compiler, you don't actually need the runtime to do specialization (see Scala's miniboxing or Dotty) and even if the runtime does specialization, you don't need it to do .NET-like reification (at least the Java engineers claim that, having plans to do primitive specialization for Java 10 or something).
> having type erasure is among the best features of Java
Until you try to use something like a List<int> without wasting incredible amounts of memory. This is even worse in a VM with custom value types (like the CLR), which also have to deal with boxing.
> For example F# doesn't support higher-kinded types because the CLR doesn't support higher-kinded types and introducing support for HKT would mean for the F# compiler to basically do type erasure by itself, which would place it at a serious disadvantage versus the host language in terms of performance.
So the solution is to push the performance disadvantage into the VM so all languages are equally screwed?
F# already does type erasure in some circumstances with Type Providers (erased vs. generated types).
F* runs on the CLR and has higher kinded polymorphism.
Also, while the JVM multi-language story is terrific, it's not like the CLI is severely wanting since it also has several world-class language implementations:
* C#
* VB (criminally underrated; it's not just C# with another costume)
* F#
* F* (real dependent types)
* C++/CLI
* Nemerle (like C#, but with real macros and pattern-matching)
> Because really, you don't really need to check whether something is an instance of "List<int>" at runtime, in a static language, unless your language sucks ;-)
So, I hate reflection with a burning passion, but this is wrong. You may not like doing it, but there are tons of legitimate use cases for RTTI.
My primary case is for runtime "light-up" of advanced features. For instance, if you deploy an application to a series of platforms that provide non-overlapping sets of capabilities, a fantastic way of dealing with this problem is to use RTTI to check whether the instance of the object is some specialized case at runtime, then cast to the specialized object to use the specific features (e.g., GPS, touch screen) that are only available on that platform.
The problem with .NET reflection is not that this dynamism exists, it's that everything is bundled together without an opt-out. I would love to have reflection split into small pieces that I could pick and choose from -- maybe I get RTTI, but I can't instantiate arbitrary generic types at runtime, for instance. Or I can't do RefEmit to JIT dynamically generated IL.
These play havoc with the runtime, forcing it to support incredibly elaborate systems just for simple features, so providing some guarantees would make the runtime's job way easier while also providing developer flexibility.
> The problem with .NET reflection is not that this dynamism exists, it's that everything is bundled together without an opt-out. I would love to have reflection split into small pieces that I could pick and choose from -- maybe I get RTTI, but I can't instantiate arbitrary generic types at runtime, for instance. Or I can't do RefEmit to JIT dynamically generated IL.
C++'s ability to selectively turn off features drives me crazy, as I either need to convince myself to live without RTTI and exceptions or live without most of the libraries that exist.
Specially given the mentality of micro-benchmarking code lines without profiling.
It is also the cause of quite a few C++ warts, because the library code needs to be written assuming those features can be disabled.
The actual reason for why you feel the need for reification is because the type systems of both C# and Java are so poor that you end up in situations where you really need to do "isInstanceOf" checks and castings. But people are missing the forest from the trees here, because these are essentially holes in the type-system, only needed because the languages are not expressive enough. Because really, you don't really need to check whether something is an instance of "List<int>" at runtime, in a static language, unless your language sucks ;-)
As to why type erasure is among the best features of Java, that's because with reification it gets hard to support other languages. It's kind of ironic, but .NET CLR was once marketed as the multi-language runtime, but time has been on the JVM's side in that regard.
For example F# doesn't support higher-kinded types because the CLR doesn't support higher-kinded types and introducing support for HKT would mean for the F# compiler to basically do type erasure by itself, which would place it at a serious disadvantage versus the host language in terms of performance. Scala supports higher-kinded types, which means you can express in Scala types such as the Monad and the Applicative, which are essential for FP in static languages, without sacrificing performance or Java interoperability. Even OCaml supports higher-kinded types.
And this isn't just about static languages. Pick any dynamic language you can think of, take your pick from Ruby, Python, Clojure and Javascript and compare the performance of the implementations on the JVM versus the CLR. OK, it's sort of an unfair comparison, given the JVM implementations have had more resources invested in them, but you know, one of the reasons for why dynamic languages work better on top of the JVM is because the JVM's bytecode is basically dynamically typed, with the last barriers being brought down along with invokedynamic. But by introducing generics reification on the other hand you end up with more static info in the bytecode that dynamic languages have to work around.
The only clear advantage to reification is the specialization for primitives that the CLR is doing for things like the standard collections. But you know, this can be a job for the compiler, you don't actually need the runtime to do specialization (see Scala's miniboxing or Dotty) and even if the runtime does specialization, you don't need it to do .NET-like reification (at least the Java engineers claim that, having plans to do primitive specialization for Java 10 or something).