6 ms·
Regardless of questions of what should or shouldn't be done, there is a serious issue with reifying generic types with type parameters that support subtyping, a
by pron 8y ago
Regardless of questions of what should or shouldn't be done, there is a serious issue with reifying generic types with type parameters that support subtyping, as the JVM does. It requires deciding on a variance policy (to determine the subtyping relationships among the generic types), and different languages choose different variance policies to fit with their overall design. Reifying generic types in this case would bake a single variance policy into the runtime, which would hinder the ability of different languages to share data structures. If the type parameters do not support subtyping, this is not a problem, and, indeed, when generic types will be instantiated with parameters of Java's upcoming value types (that cannot be subtyped), they will likely be reified and specialized.
- int_19h 8y ago> It requires deciding on a variance policy (to determine the subtyping relationships among the generic types) Why does it? In C#, for example, variance policy for generic type parameters is expressed explicitly - invariant by default, covariant or contravariant if you ask for it. While C# also has reified generics, I don't see why this couldn't be done with non-reified ones.
- sreque 8y agoC#'s reified generics are actually a problem for other languages that want to run on the CLR, and in fact those languages generally have data structures with no reified generics (the data structures just store type Object and do perform dynamic casting). In order to work with the CLR bytecode generated outside of their compilers, these languages unfortunately also have to provide providence for understanding generic data structures. The JVM has been more successful than the CLR at hosting other languages, and having unreified generics I believe has contributed to that success. For instance, I have seen both the creator of Scala and one of the JRuby contributors both say they prefer JVM generics to CLR generics.
- int_19h 8y agoI still don't see what that has to do with variance policy specifically. You're basically saying that reified generics are bad, because they require all languages on a given runtime to be aware of them to interoperate. Which is a valid point, but if you are aware of generics, then CLR lets you express whatever variance you want - including none. And so long as your language has CLR interface casts at all, you automatically get support for that variance at runtime, even if the type system of the language doesn't reflect such relations statically; so e.g. you might not be able to implicitly convert IEnumerable<Derived> to IEnumerable<Base>, but you should always be able to explicitly cast IEnumerable<Derived> -> Object -> IEnumerable<Base>.
- pron 8y agoThe issue is not the variance of a specific data structure, but of how variance itself is expressed, which differs from one language to another. What you described is one variance policy.
- int_19h 8y agoCan you give an example of a language variance policy that is 1) sound, and 2) cannot be mapped onto CLR variance declarations? I'll grant you that it's possible to come up with policies that are not sound (e.g. covariant mutable collections, like both Java and .NET do with built-in arrays) that cannot be so mapped. But how many languages have run into this as an issue in practice? By far the most common policy, in any case, is "no variance", which maps just fine. And you don't need to support variance to consume .NET interfaces that do have variance declarations, for the reasons I have explained in my earlier comment - so it doesn't really affect interop.
- pron 8y agoSure. Java uses use-site variance. Clojure has an "all ways" variance, as it's untyped. But Java, Kotlin (mostly declaration-site, some use-site) and Clojure all share the same data types without any need for runtime conversions. In general, you want to bake as little of the type-system as possible into the runtime; just as much as necessary to provide your most essential features. Reifying generic types with subtypable indices is a feature with very little benefit and a pretty big cost in lost flexibility.
- gracenotes 8y agoReification doesn't just impose a substantial complexity cost on dynamic languages in the CLR, either. See this F# feature request from 2011 that is effectively unimplementable: https://visualstudio.uservoice.com/forums/121579-visual-studio-ide/suggestions/2228766-add-higher-order-generics-to-f-type-classes https://visualstudio.uservoice.com/forums/121579-visual-stud... As GP mentions, Haskell is a great example of achieving both superb type safety and superb type expression with type erasure. That said, sometimes you don't want superb type safety - things like dependency injection and mocks are examples of frameworks you can use in languages with some amount of type reification without restructuring your entire codebase. Java/JVM achieve a decent balance here imo.