2013-01-19 35 views
5

Trong Scala (2.10) nếu tôi yêu cầu List(1.0, 2) thì tôi nhận được List[Double], như mong đợi.Loại Scala bất ngờ Loại

Nhưng ...

nếu tôi yêu cầu một Map(1.0 -> 'A', 2 -> 'B') tôi nhận được một Map[AnyVal, Char]. Tôi muốn các khóa có loại Double. Yêu cầu Map[Double, Char](1.0 -> 'A', 2 -> 'B) cho loại không phù hợp trên '2'.

Điều này tôi thấy khó hiểu nhất! Nó không phải là không phù hợp?

Ngoài ra:List[(Double, Char)]((1.0, 'A'), (2, 'B')).toMap cung cấp cho tôi bản đồ [Double, Char].

+0

Sao chép trên 2.9.2. –

Trả lời

6

Nếu bạn nhìn vào Scala của type hierarchy, bạn có thể thấy rằng các loại nguyên thủy không phải là trong một sub-type/mối quan hệ siêu-type. Tuy nhiên, có một cơ chế được gọi là mở rộng số, ví dụ: cho phép bạn gọi một phương thức lấy đối số Double, bằng cách chuyển vào nói số Int. Sau đó, Int sẽ tự động được "mở rộng" thành Double.

Đây là lý do mà List(1.0, 2) cung cấp cho bạn List[Double].

Nhưng hàm tạo Map mất Tuple[A, B] đối số. Mở rộng số không áp dụng cho các loại đơn đặt hàng cao hơn, do đó, suy luận loại mục tiêu không hoạt động cho bạn nếu bạn trộn các loại số.

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

Hơn nữa, mũi tên điều hành -> được theo cách của bạn:

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

Đây là một hạn chế của những suy luận kiểu tôi nghĩ. Viết một tuple a -> b là cú pháp đường cho (a, b), được cung cấp bởi phương pháp ngầm any2ArrowAssoc trên Predef.Nếu không có gián tiếp này, nếu bạn xây dựng Tuple2 trực tiếp, nó hoạt động:

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

Vì vậy, việc mở rộng số vẫn không hoạt động, nhưng ít nhất bạn có thể thực thi các loại:

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

Ví dụ cuối cùng cho thấy việc mở rộng số làm việc:

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

Trong trường hợp Danh sách, không có khai báo loại, Scala xem xét tất cả các phần tử và cố gắng tìm loại phổ biến. Trong trường hợp của bạn, vì Int có thể được chuyển đổi thành Double, nó chuyển đổi danh sách hỗn hợp của bạn thành một List [Double] quảng bá Int của bạn.

Trình xây dựng bản đồ có một loạt 2 bộ dữ liệu. Bạn sẽ nhận được hành vi tương tự, nếu bạn vừa xây dựng danh sách các bộ dữ liệu:

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

Tuple2 [Int, String] không thể tự động được quảng bá lên Tuple2 [Double, String]. Trong trường hợp này, bạn sẽ cần phải giúp trình biên dịch ra một chút với một tuyên bố loại:

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)) 

hoặc

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

hoặc trong trường hợp của bạn:

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) 

Đối với một số lý do , bằng cách sử dụng khai báo kiểu trên Bản đồ không hoạt động. Bạn không chắc chắn lý do tại sao:

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")