2013-01-19 16 views
5

W Scali (2.10), jeśli poproszę o List(1.0, 2), otrzymam List[Double], zgodnie z oczekiwaniami.Nieoczekiwana Scala Typ mapy

Ale ...

jeśli pytam o Map(1.0 -> 'A', 2 -> 'B') dostaję Map[AnyVal, Char]. Chciałbym, żeby klucze były typu Double. Pytanie o Map[Double, Char](1.0 -> 'A', 2 -> 'B) podaje niedopasowanie typu na "2".

To wydaje mi się najbardziej mylące! Czy to nie jest niespójne?

Oprócz:List[(Double, Char)]((1.0, 'A'), (2, 'B')).toMap daje mi mapę [Double, Char] choć.

+0

Możliwość powtórnego odtworzenia również w wersji 2.9.2. –

Odpowiedz

6

Jeśli spojrzeć na Scala type hierarchy, można zobaczyć, że prymitywne typy nie są podtypem/relacji super-type. Istnieje jednak mechanizm nazywany rozszerzaniem numerycznym, który pozwala na przykład wywołać metodę, która przyjmuje argument Double, przekazując, powiedzmy, Int. Teraz Int jest automatycznie "poszerzany" do Double.

Jest to powód, dla którego List(1.0, 2) podaje List[Double].

Ale konstruktor Map pobiera argumenty . Rozszerzanie numeryczne nie dotyczy typów wyższego rzędu, więc wnioskowanie typu docelowego nie działa w przypadku mieszania typów liczbowych.

case class Test[A](tup: (A, Char)*) 
Test(1.0 -> 'A', 2 -> 'B') // AnyVal 

Ponadto operator strzałka -> dostaje na swój sposób:

Test[Double](2 -> 'B') // found: (Int, Char) required: (Double, Char) 

To kolejne ograniczenie typu wnioskowania myślę. Pisanie krotki a -> b jest cukrem syntaktycznym dla (a, b), dostarczonym przez niejawną metodę any2ArrowAssoc na Predef.Bez tego zadnie, jeśli skonstruować Tuple2 bezpośrednio, to działa:

Test[Double]((2, 'B')) 

więc poszerzenie numeryczny nadal nie działa, ale przynajmniej można wymusić typ:

Map[Double, Char]((1.0, 'A'), (2, 'B')) 

Ostatni przykład pokazujący rozszerzenie numeryczne działa:

def map[A, B](keys: A*)(values: B*) = Map((keys zip values): _*) 
map(1.0, 2)('A', 'B') // Map[Double, Char] 
3

W przypadku listy, bez deklaracji typu, Scala przegląda wszystkie elementy i próbuje znaleźć typ wspólny. W twoim przypadku, ponieważ Int może zostać przekonwertowany na Double, konwertuje twoją mieszaną listę na listę [Double] promującą twój jeden Int.

Konstruktor map pobiera serię 2-krotnych. co można uzyskać takie samo zachowanie, jeśli tylko skonstruowane listę krotek:

scala> List((1, "one"), (2.0, "two.oh")) 
res0: List[(AnyVal, String)] = List((1,one), (2.0,two.oh)) 

Tuple2 [Int, String] nie może automatycznie być promowany do Tuple2 [Double, String]. W tym przypadku trzeba pomóc kompilator się nieco z deklaracją typu:

scala> val x: List[(Double, String)] = List((1, "one"), (2.0, "two.oh")) 
x: List[(Double, String)] = List((1.0,one), (2.0,two.oh)) 

lub

scala> val x = List[(Double, String)]((1, "one"), (2.0, "two.oh")) 
x: List[(Double, String)] = List((1.0,one), (2.0,two.oh)) 

lub w przypadku:

scala> val x = List[(Double, String)]((1, "one"), (2.0, "two.oh")).toMap 
x: scala.collection.immutable.Map[Double,String] = Map(1.0 -> one, 2.0 -> two.oh) 

Dla jakiegoś powodu , używanie deklaracji typu na mapie nie działa. Nie wiem, dlaczego:

scala> val x = Map[Double, String](1 -> "one", 2.0 -> "two.oh") 
<console>:7: error: type mismatch; 
found : (Int, String) 
required: (Double, String) 
    val x = Map[Double, String](1 -> "one", 2.0 -> "two.oh")