2012-01-06 27 views
6

następujące oświadczenie kompiluje i działa zgodnie z oczekiwaniami:Nie można dodać element do mapy za pomocą dynamicznego typu wstawionej do klucza

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

Jednak gdy próbuję dodać do mapy:

map + ((3,4)) 

lub

map + (("Bye", 4)) 

potem dostać niedopasowanie typu:

znaleziono: java.lang.String ("Do widzenia")

wymagane: _ $ 1 w przypadku typu _ $ 1>: Int sznurkiem

Gdybym osłabić podpis typu, aby umożliwić Any jako typ klucza, to wszystko działa zgodnie z oczekiwaniami.

Moja intuicja mówi, że ma to związek z niezmiennością klucza Map, i że 1 $ jest w jakiś sposób naprawiany jako szczególny supertyp o Int with String, ale nie jestem z tego zadowolony. Czy ktoś może wyjaśnić, co się dzieje?

Edited by dodać:

W przypadku, gdy zastanawiasz się, gdzie ten powstaje, to jest podpis, że pojawi się, jeśli można zrobić coś takiego:

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

Odpowiedz

9

nie zrozumiałeś Int with String. To nie jest Unia Int i String, to jest przecięcie, a dla Int i String jest pusta. Nie zbiory wartości, które są Int z zestawem wartości, które są łańcuchami, ale zbiór wartości, które mają właściwości Int z cechami również String. Nie ma takich wartości.

Można użyć Either[Int, String] i mieć Map[Left(1) -> 2, Right("Hello") -> 3). Albo nie jest to dokładnie Unia, to dyskryminowany związek, A + B, a nie A U B. Możesz uchwycić różnicę w tym, że Albo [Int, Int] to nie to samo co Int. Jest on w istocie izomorficzny do (Int, Boolean): masz Int, i wiesz, po której stronie też jest. Kiedy A i B są rozłączne (jak Int i String są) A + B i A U B są izomorficzne.

Lub (nie dla osób o słabym sercu) można spojrzeć na a possible encoding of union types Milesa Sinaina. (Nie jestem pewien, czy mógłbyś użyć tego z istniejącą Mapą klas, a jeszcze mniej pewnie, niż powinieneś spróbować, ale mimo to robi najbardziej interesującą lekturę).


Edit: Przeczytaj pytanie i kodu sposób zbyt szybko, przepraszam

dolnej związany Int with String jest taka sama jak Nothing, więc Map[_ >: Int with String, Int], jest taka sama jak Map[_ >: Nothing, Int] i jak Nothing dolna granica jest domyślnie jest to Map[_, Int]. Twój rzeczywisty Map jest Map[Any, Int]. Mógłbyś dodać klucz boolowski, działa on również, pomimo ciągu Int z ciągiem. A Map[Any, Int] można wpisać jako Map[_, Int], więc twoja deklaracja val działa. Ale twoje pisanie traci wszystkie informacje o typie klucza.Nie wiedząc, jaki jest typ klucza, nie można dodawać (ani pobierać) niczego z tabeli.

Górna granica nie byłaby lepsza, ponieważ wtedy nie ma żadnego możliwego klucza. Nawet początkowa deklaracja val nie działa.


Edycja 2: czasowo if (true) Map(1 -> 2) else Map("1" -> 2)

To nie jest to samo, co Map(1 -> 2, "1" -> 2). To było prostsze, po prostu jest typowym nadprzyrodzonym typem Int i.

Z drugiej strony Map(1 -> 2) to Map[Int, Int] i Map["1", 2] a Map[String, Int]. Występuje problem ze znalezieniem wspólnego nadtypu dla Map[Int, Int] i Map[String, Int], który nie znajduje wspólnego nadtypu Int i String.

Eksperymentujmy. Map jest kowariantny w swoim drugim parametrze. Jeśli używasz Int i String jako wartości zamiast klawiszy:

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

Z kowariancji, to po prostu trwa supertypem wspólną wszystkich parametrów typu.

z typem kontrawariantny:

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] 

trwa przecięcia A with C. Po B jest podtypem A, A z B tylko B.

Teraz z non-wariantem parametru Mapie gdy oba typy są związane

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

Taki typ nie jest bezużyteczny. Możesz uzyskać dostęp lub dodać wartości za pomocą klawiszy typu B. Ale nie można uzyskać dostępu do wartości kluczy A. Ponieważ jest to, co masz w rzeczywistej mapie (z powodu true), ciężko. Jeśli uzyskasz dostęp do keySet, zostanie wpisany Set[A]. Masz niepełne informacje na temat rodzaju kluczy, a to, co możesz zrobić, jest ograniczone, ale jest to konieczne ograniczenie, biorąc pod uwagę ograniczoną wiedzę na temat rodzaju mapy. Przy Int and String masz minimalną informację, z dolną granicą Any i górną granicą równoważną Nothing. Górna granica nic nie pozwala wywołać procedury, która przyjmuje klucz jako parametr. Nadal można pobrać keySet, z dolną granicą typu Set[Any], Any.

+0

Nie sądzę, że to jest problem - "Int with String" jest związana z typem klucza, a nie z samym typem. Mogę użyć tego jako związanego w wielu innych sytuacjach. – Submonoid

+0

Kodowanie rzeczy typu związków jest niesamowite, nawiasem mówiąc, ale nie jest to szczególnie istotne dla pytania - w szczególności interesuje mnie, dlaczego przypisanie jest ważne, ale próba dodania czegokolwiek innego nie powiedzie się. – Submonoid

+0

Myślę, że jest to całkowicie bezużyteczne ograniczenie, patrz uzupełnienie do odpowiedzi. –