Is it possible to change the variance of a base class / trait in Scala?

I would like to get an immutable map from Scala. It is defined as such:

trait Map[A, +B] 

Unfortunately, my implementation must be invariant in B. I tried the following, but to no avail:

 def +(kv : (A, B)) : MyMap[A, B] = { ... } override def +[B1 >: B](kv : (A, B1)) : MyMap[A, B1] = throw new IllegalArgumentException() 

Maybe there is a trick with @uncheckedVariance ?

+6
source share
2 answers

Getting rid of covariance would generally be, of course, unreasonable and not allowed. Given m: Map[A, String] and v : Any , you can do val mm : Map[A, Any] = m + v . This is indicated by the definition of Map , and all developers should follow. Your class may be invariant, but it must implement the full covariant Map interface.

Now redefining + to throw an error is a completely different story (not very good yet). The problem with your new + method is that after erasing the generics, it has the same signature as the other + method. There is a trick: add an implicit parameter, so that you have two parameters in the signature, which distinguishes it from the first.

 def +(kv : (A,B))(implicit useless: A <:< A) : MyMap[A,B] 

(it doesn't really matter what implicit parameter you are looking for if it is found. implicit useless: Ordering[String] works just as well)

By doing this, you have the usual overload problem. If you add B without a compiler, knowing that it is, the reject method is called. It might be better to do a type check there so that the B instances are accepted. This will require a manifesto [B] on your card.

+1
source

The problem is that if you get an invariant version from an immutable card, you will violate type security. For instance:

 val dm = DiotMap(1 -> "abc") val m: Map[Int, Any] = dm 

This ad is valid because Map is covariant. If your collection cannot handle covariance, what will happen when I use m ?

+3
source

Source: https://habr.com/ru/post/895268/


All Articles