2012-01-06 48 views
15

Trong Scala một Set là một chức năng:Tại sao đặt chức năng?

trait Set[A] extends (A => Boolean) 

này làm cho nó không thể có một hiệp biến bất biến Set vì loại A xảy ra ở vị trí contravariant. Ngược lại Seq không được định nghĩa là hàm. Đã có một số nội dung về câu hỏi tại sao Bộ và Seqs được thiết kế theo cách này:

Một câu trả lời nói rằng lý do cho điều này là nền tảng toán học . Nhưng câu trả lời này không được giải thích thêm một chút. Vì vậy, những lợi thế cụ thể để xác định một Set là một chức năng hoặc những gì sẽ là những bất lợi nếu nó được thực hiện khác nhau?

+4

Tôi nghĩ [câu trả lời] (http://stackoverflow.com/a/6183115/358642) đối với câu hỏi đầu tiên bạn liên kết nói nhiều hơn các câu hỏi khác. – Philippe

+0

Nhân tiện, tại sao nếu tôi gọi hàm 'foo' trong câu hỏi được liên kết đầu tiên là foo (Set (" asd ")), nó biên dịch? – Rogach

+0

Tôi nghĩ rằng đó là vì phương pháp 'apply' trong [' SetFactory'] (https://github.com/scala/scala/blob/master/src/library/scala/collection/generic/SetFactory.scala) là linh hoạt đủ để tạo ra các tập hợp các loại phần tử khác nhau mà chỉ là các đối số. – Philippe

Trả lời

11

Tập hợp loại Set[A] phải có phương pháp kiểm tra xem một phần tử thuộc loại A có trong tập hợp không. Phương thức này (apply) phải có tham số kiểu A đại diện cho phần tử đó và tham số đó ở vị trí contravariant. Điều này có nghĩa là các tập không thể là biến thể trong tham số kiểu A của chúng. Vì vậy, nó không phải là mở rộng của giao diện chức năng mà làm cho nó không thể có các tập bất biến biến đổi, đó là sự tồn tại của phương pháp contravariant apply.

Và vì lý do thuận tiện, bạn có thể mở rộng giao diện Function1 để có thể truyền các bộ xung quanh và coi chúng là các hàm.

Ngược lại, trừu tượng chuỗi không có phương pháp kiểm tra nếu một phần tử nằm trong chuỗi, nó chỉ có phương pháp lập chỉ mục - apply lấy chỉ số nguyên và trả về phần tử tại chỉ mục đó. Chuỗi cũng được định nghĩa là hàm, nhưng chức năng của loại Int => A (là biến thể trong A), không phải là A => Boolean, làm bộ.

Nếu bạn muốn biết thêm về mức độ an toàn kiểu sẽ bị hỏng nếu tập hợp được định nghĩa là biến thể trong thông số loại A, hãy xem ví dụ này trong đó triển khai bộ thực hiện một số văn bản cho các thành viên riêng vì lý do đệm bộ nhớ tra cứu (dưới đây @uV là chú thích mà vô hiệu hóa việc kiểm tra đúng và expensiveLookup là có nghĩa là để mô phỏng một cuộc gọi đến một tấm séc tính toán tốn kém nếu một yếu tố nằm trong tập):

import annotation.unchecked.{uncheckedVariance => uV} 

trait Set[+A] { 
    def apply(elem: A @uV): Boolean 
} 

class CachingSet[+A >: Null] extends Set[A] { 
    private var lastLookup: (A @uV, Boolean) = (null, false) 
    private def expensiveLookup(elem: A @uV) = (elem, true) 
    def apply(elem: A @uV): Boolean = { 
    if (elem != lastLookup._1) lastLookup = expensiveLookup(elem) 
    lastLookup._2 
    } 
    def lastQueriedElement: A = lastLookup._1 
} 

object Main extends App { 
    val css = new CachingSet[String] 
    val csa: CachingSet[AnyRef] = css 

    csa.apply(new AnyRef) 
    val s: String = css.lastQueriedElement // you'll get a ClassCastException here 
} 
6

Ngược lại Seq không được định nghĩa là một hàm.

Không đúng sự thật.

Seq[T] extends (Int) => T 
+0

Điều đó thật đáng ngạc nhiên.Tôi đã không nhận thấy rằng vì các tham số kiểu không xảy ra ở vị trí contravariant. Chi tiết đẹp! – sschaef

+0

Bạn nói đúng, chúng mở rộng một phần chức năng. Tôi đã sửa chữa điều đó. – axel22

Các vấn đề liên quan