2012-01-06 43 views
6

下面的語句編譯罰款,地圖和按預期工作:無法添加成員使用動態混入型爲重點

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

但是,如果我嘗試添加到地圖:

map + ((3,4)) 

map + (("Bye", 4)) 

然後我得到一個類型不匹配:

發現:java.lang.String中( 「再見」)

要求:_ $ 1,其中類型_ $ 1>:詮釋與字符串

如果我削弱類型簽名允許Any作爲鍵的類型,那麼這一切都按預期工作。

我的直覺說,這是地圖的主要類型的nonvariance做,那_ $ 1莫名其妙地被固定爲Int with String特定父,但我不是特別滿意這個。任何人都可以解釋發生了什麼?

編輯補充:

如果你想知道在哪裏出現這種情況,這是如果你做這樣的事情,你得到的簽名:

val map = if (true) Map(1 -> 2) else Map("1" -> 2) 

回答

9

你誤會Int with String。它不是Int和String的聯合,它是交集,而Int和String則是空的。不是以字符串值爲集合的Int值集合,而是以String爲特徵的具有Int特性的值集合。沒有這樣的價值。您可以使用Either[Int, String],並有Map[Left(1) -> 2, Right("Hello") -> 3)。要麼是不完全是聯盟,而是歧視的聯盟,A + B,而不是A/B。你可以把握區別在於[Int,Int]與Int不同。它實際上與(Int,布爾)同構:你有一個Int,並且你知道它是哪一邊。當A和B不相交時(如Int和String所示)A + B和A U B是同構的。

或者(不是因爲心疼)你可以看看Miles Sabin的a possible encoding of union types。 (我不確定你是否可以在預先存在的Map Map中使用它,甚至不如你應該嘗試的那樣確定,但它仍然是最有意思的閱讀方式)。


編輯:看了你的問題和代碼太快了,對不起

你下界Int with String相同Nothing,所以Map[_ >: Int with String, Int],相同Map[_ >: Nothing, Int]並作爲Nothing下界暗示,這是Map[_, Int]。您的實際MapMap[Any, Int]。你可以添加一個布爾鍵,它也可以工作,儘管用字符串Int。 A Map[Any, Int]可以輸入爲Map[_, Int],以便您的val聲明有效。但是你的輸入會丟失關於密鑰類型的所有信息。不知道鍵的類型是什麼,你不能從表中添加(也不能檢索)任何東西。

的上界就沒有更好的,因爲那麼有沒有可能的密鑰。即使最初的val聲明也失敗了。


編輯2:關於if (true) Map(1 -> 2) else Map("1" -> 2)

這是不一樣的東西Map(1 -> 2, "1" -> 2)。這是簡單的,只是一個Map[Any, Int],爲AnyIntString更大的公用超類型。

另一方面,Map(1 -> 2)Map[Int, Int]Map["1", 2] a Map[String, Int]。沒有發現用於Map[Int, Int]Map[String, Int]一個共同的超類型,這是沒有找到的IntString一個共同的超類型的問題。

讓我們的實驗。 Map在其第二個參數中是協變的。如果您使用IntString的值,而不是鍵:

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

協方差,它只是通吃類型參數的公用超類型。

隨着逆變類型:

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] 

花費的交點A with C.BA一個亞型,AB只是B

現在,隨着地圖的非變量參數,當兩種類型都與

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

這種類型也不是一無是處。您可以使用B類型的密鑰訪問或添加值。但是您無法訪問密鑰A的值。由於這是你在實際地圖中的(因爲true),所以很難運氣。如果您訪問keySet,將會輸入Set[A]。您對密鑰類型的信息不完整,您可以執行的操作有限,但由於您對地圖類型的知識有限,因此這是一個必要的限制。使用Int and String,您獲得的信息極少,下限爲Any,上限爲Nothing。 Nothing上限使得不可能調用以鍵爲參數的例程。您仍然可以檢索keySet,類型爲Set[Any],Any爲下限。

+0

我不認爲這是問題 - '詮釋與String'是關鍵,而不是類型本身的類型束縛。我可以在很多其他情況下使用它作爲綁定。 – Submonoid 2012-01-06 12:36:29

+0

順便說一下,聯合類型的編碼非常棒,但與問題無關 - 特別是,我對賦值爲什麼有效但試圖添加其他任何失敗感興趣。 – Submonoid 2012-01-06 12:46:58

+0

我認爲這是一個完全無用的界限,請參閱補充來回答。 – 2012-01-06 13:49:48