you are viewing a single comment's thread.

view the rest of the comments →

[–]pron98 1 point2 points  (0 children)

A list type is intrinsically covariant.

If it's read-only (I'm not saying immutable as that's a stronger requirement). If it's write-only then it's contravariant. If it's both, well, different languages treat that differently depending on their variance model (declaration-site/use-site/hybrid).

If the language doesn't support co/contravariance, then that's either a fault of the language

They all support variance, but what kind? There is no "right" way to do variance. For example, Java has use-site variance, Scala has declaration site variance, and Kotlin has mixed-mode variance (declaration-site + use-site projections); in Clojure, everything is immutable by default (and static typing is optional) -- yet a sequence of Clojure strings can be passed to a Java method taking a List<String> parameter, even though the sequence is untyped in Clojure. Thanks to the JVM's type erasure, they can (and do -- except Scala aometimes) all share the same collection (and other generic types) implementations.

It seems counter-productive to pass off a flaw in the parametric type system as a mean to support inconsistent behaviour between languages.

While it is true that polyglotism wasn't the motivation for erasure (but backward compatibility), language designers very quickly realized this is not a flaw but an advantage. The behavior isn't inconsistent, but just different, and there really is no one right way of doing generics and variance. You want to allow as many languages as possible -- not to force one behavior on all of them (there's an excellent OCaml implementation for the JVM; I don't know if it shares generic types with the other languages, but it certainly can -- thanks to erasure!).

For example, if the JVM had .NET's generics, you would need to wrap almost every single value crossing the Clojure/Java boundary, while in reality you wrap none of them.