4 ms·
C#'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
by sreque 8y ago
C#'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.
- int_19h 8y agoYou can do use-site declarations for upper boundaries in C# as well with "where". There's no equivalent to "super", though. The benefit of having this on VM level is the same as having classes on VM level - type safety rules are consistent and uniform, and you can't write a verified (in IL terms) component in one language that can be suddenly made unsafe by another language. Also, I don't see how you'd implement variance for is/as (instanceof and casts in Java) without runtime support; JVM languages don't have this issue only because there's no way to query an object for implementation of a particular instantiation of a generic interface on JVM in general, due to type erasure. But in C#, you can e.g. do: var xs = new List<Derived>(); var ys = xs as IEnumerable<Base>(); and it'll work, even if these two lines are in totally different components. How would you do this in a typesafe manner (i.e. "as" only works if Derived is actually inherited from Base) on JVM?
- pron 8y ago> How would you do this in a typesafe manner (i.e. "as" only works if Derived is actually inherited from Base) on JVM? On the JVM or in Java? In Java you don't do it in such a safe manner. Whether you should or shouldn't be able to is another matter. If you want to write a JVM language where you want to do that safely (like Ceylon), you just reify your type -- at the cost of interop and reuse. Opting into reification is cheaper (at runtime) than opting out.
- int_19h 8y ago
- 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.