Unable to add item to map using dynamic mixin type for key

The following statement compiles and works as expected:

val map : Map[_ >: Int with String, Int] = Map(1 -> 2, "Hello" -> 3) 

However, if I try to add to the map:

 map + ((3,4)) 

or

 map + (("Bye", 4)) 

then I get a type mismatch:

found: java.lang.String ("Bye")

required: _ $ 1 where type _ $ 1>: Int with string

If I loosened the type signature to allow Any as the key type, then it all works as expected.

My intuition says that this is due to the invariance of the card key type and that _ $ 1 is somehow fixed as a special supertype of Int with String , but I'm not very happy with it. Can anyone explain what is happening?

Edited to add:

If you're curious where this comes from, this is the signature you get if you do something like:

 val map = if (true) Map(1 -> 2) else Map("1" -> 2) 
+6
source share
1 answer

You misunderstand Int with String . This is not a union of Int and String, this is the intersection, but for Int and String it is empty. Not a set of values ​​that are Int with a set of values ​​that are strings, but a set of values ​​that also have Int characteristics with String characteristics. There are no such values.

You can use Either[Int, String] and have Map[Left(1) -> 2, Right("Hello") -> 3) . Either this is not a Union, but a discriminatory union, A + B, not AU B. You can understand the difference in that either [Int, Int] is not the same as Int. It is actually isomorphic (Int, Boolean): you have Int, and you know which side it is on too. When A and B are disjoint (like Int and String) A + B and AUB are isomorphic.

Or (not for the faint of heart) you can take a look at the possible coding of union types by Miles Sabin. (I'm not sure if you can really use this with a pre-existing class map and even less confidently than you should even try, but nonetheless it does the most interesting reading).


Change Read your question and code too fast, sorry

Your bottom border, Int with String same as Nothing , so Map[_ >: Int with String, Int] same as Map[_ >: Nothing, Int] , and Map[_, Int] meant as the bottom border of Nothing . Your actual Map is Map[Any, Int] . You could add a logical key, it works too, despite the Int with String. A Map[Any, Int] can be entered as Map[_, Int] , so your val declaration will work. But your input loses all information about the key type. Not knowing what type of key is, you cannot add (or retrieve) anything from the table.

An UpperBound would not be better, since then there is no key. Even the initial val declaration does not work.


Edit 2 : regarding if (true) Map(1 -> 2) else Map("1" -> 2)

This is not the same as Map(1 -> 2, "1" -> 2) . It was simpler, just a Map[Any, Int] , since Any is the more common supertype of Int and String .

On the other hand, Map(1 -> 2) is Map[Int, Int] and Map["1", 2] a Map[String, Int] . There is the problem of finding a common supertype for Map[Int, Int] and Map[String, Int] , which does not find a common supertype of Int and String .

Let the experiment. Map covariant in the second parameter. If you use Int and String as values, not keys:

 if (true) Map(1 -> 2) else Map(1 -> "2") res1: scala.collection.immutable.Map[Int, Any] 

With covariance, it simply takes a common supertype of all type parameters.

With contravariant type:

 class L[-T] object L{def apply[T](t: T) = new L[T]) class A class B extends A class C if (true) L(new A) else L(new C) res2: L[A with C] if (true) L(new A) else L(new B) res3: L[B] 

intersection of A with C. is required A with C. When B is a subtype of A , A with B is simply B

Now with the non-invariant Map parameter, when the two types are connected

 if (true) Map(new A -> 1) else Map(new B -> 1) res4: scala.collection.immutable.Map[_ >: B <: A, Int] 

This type is not useless. You can get or add values ​​using type B keys. But you cannot access the values ​​of the keys A Since this is what you have on the actual map (due to true ), hard luck. If you select keySet , it will be typed Set[A] . You have incomplete information about the type of keys, and what you can do is limited, but this is a necessary limitation, given the limited knowledge of the type of card. With Int and String , you have minimal information with the lower bound Any and the upper bound equivalent to Nothing . The upper bound of Nothing does not allow calling a procedure that takes a key as a parameter. You can still get keySet , with the type Set[Any] , Any , which is the lower bound.

+9
source

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


All Articles