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.