Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Intersect Scala set with set of subtype

Tags:

generics

scala

Why doesn't this function compile?

case class MyType(n: Int)
def intersection(s1: Set[MyType], s2: Set[_ <: MyType]) =
  (s1 & s2)

I get the following error:

error: type mismatch; found : Set[_$1] where type _$1 <: MyType required: scala.collection.GenSet[MyType] Note: _$1 <: MyType, but trait GenSet is invariant in type A. You may wish to investigate a wildcard type such as _ <: MyType. (SLS 3.2.10) (w & r)

Is there a simple way to "promote" the second argument to type Set[MyType] without using asInstanceOf?

like image 457
tba Avatar asked Aug 02 '13 07:08

tba


2 Answers

This is because Set is defined as Set[A]. It is in-variant and not co-variant.

& is defined as

def &(that: GenSet[A]): Set[A]

It expects and argument of type Set[A]. But you are instead providing Set[_ <: MyType].

Set[_ <: Mytype] is co-variant to Set[MyType]. But as the declaration says that the argument should be in-variant i.e. Set[MyType], hence the error.

PS: You can think of co-variance as type converting from narrow to broader. For ex: if Dog extends Animal and if you do Animal a = new Dog(), you have a Dog (narrow) converting to Animal (broader). Above it expects invariant type. i.e. if it expects Animal, you can only provide an Animal only. Another example is java.util.ArrayList which is in-variant.

like image 119
Jatin Avatar answered Sep 18 '22 12:09

Jatin


A Set is not covariant on its type parameter.

So a simple solution is to convert to List (which is covariant):

def intersection(s1: Set[MyType], s2: Set[_ <: MyType]) =
    s1.toList.intersect(s2.toList).toSet
like image 45
Beryllium Avatar answered Sep 20 '22 12:09

Beryllium