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
.
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
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
Myślę, że jest to całkowicie bezużyteczne ograniczenie, patrz uzupełnienie do odpowiedzi. –