Covariant and contravariant generic type parameters

I had hoped Swift 2 would add support for covariant and contravariant generic type parameters, but I can't find any mention of this.


It seems the new lightweight generics for Objective-C do support covariance and contravariance, with NSArray being defined as:

@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>


Will a similar feature be available in Swift soon?


I'm aware Using Swift with Cocoa and Objective-C mentions lightweight generics other than those in the Foundation collection classes are currently ignored when imported in Swift, but it seems this is would be a temporary limitation.

Replies

Well I still don't get what is missing here. Inheritance works perfectly fine. So where comes the need for genereics in here? 😕

I'm not sure that there's a need for it - although I'm down the wikipedia rabbit-hole looking at how other languages do it - but it is interesting that collection types have a different implementation to structs I define myself. I mean, if that's how the standard library works it must have some advantage? I suppose I'll just have to find out what the C# docs have to say about it.

Let me try to explain.


Covariance and contravariance aren't about whether you can store a Dog instance in an Array<Animal> (given that Dog is a subclass of Animal). Like you said, that should work just fine.


Imagine you have a generic class, Foo<T>. We know that Dog is a subclass of Animal, so you can use a Dog anywhere you can use an Animal. The question is, can you use a Foo<Dog> anywhere you can use a Foo<Animal>? The answer is, it depends.


If Foo is covariant, the answer is yes. If Dog is a subclass of Animal, then Foo<Dog> should be considered a subclass of Foo<Animal>.


If Foo is contravariant, the answer is no, and it's the opposite: a Foo<Animal> should be considered a subclass of Foo<Dog>.


If Foo is invariant, the answer is no, and Foo<Animal> and Foo<Dog> have no subclass/superclass relation to each other.


Which case is true depends on what you want Foo to do, and is something the creator of the class decides. For example, if Foo is a class representing an immutable list, then Foo can be declared as covariant. (So, you should be able to use a List<Dog> anywhere you can use a List<Animal>.) However, is Foo is a class representing a mutable list, and you declare it as covariant, you can get yourself into trouble. Here is a Stack Overflow answer where someone explains why the (mutable) List<T> in Java isn't covariant, and what sort of problem you could get into if you made it covariant: http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p/2745301#2745301.


I hope this makes things a little clearer.

  1. class FetchedResults<T> {
  2. func add(object: T) {...}
  3. }
  4. let animals = FetchedResults<Animal>()
  5. animals.add(Animal())
  6. animals.add(Dog()) // This doesn't work


That's incorrect. It does work.


What doesn't work is this.

func someFunc(param: FetchResults<Animal>) {

}

someFunc(animals) // this does work

let dogs = FetchResults<Dog>()

someFunc(dogs) // this doesn't work