Scala bối cảnh và giới hạn xem là gì?


267

Nói một cách đơn giản, bối cảnh và giới hạn xem là gì và sự khác biệt giữa chúng là gì?

Một số ví dụ dễ làm theo cũng sẽ rất tuyệt!

Câu trả lời:


477

Tôi nghĩ rằng điều này đã được hỏi, nhưng, nếu vậy, câu hỏi không rõ ràng trong thanh "liên quan". Vì vậy, đây là:

Giới hạn xem là gì?

Một khung nhìn bị ràng buộc là một cơ chế được giới thiệu trong Scala để cho phép sử dụng một số loại A như thể nó là một loại B. Cú pháp điển hình là:

def f[A <% B](a: A) = a.bMethod

Nói cách khác, Anên có một chuyển đổi ngầm định thành Bcó sẵn, để người ta có thể gọi Bcác phương thức trên một đối tượng kiểu A. Cách sử dụng phổ biến nhất của giới hạn lượt xem trong thư viện chuẩn (trước Scala 2.8.0, dù sao), là với Ordered, như sau:

def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b

Bởi vì người ta có thể chuyển đổi Athành một Ordered[A]và vì Ordered[A]định nghĩa phương thức <(other: A): Boolean, tôi có thể sử dụng biểu thức a < b.

Xin lưu ý rằng giới hạn lượt xem không được chấp nhận , bạn nên tránh chúng.

Giới hạn bối cảnh là gì?

Giới hạn ngữ cảnh được giới thiệu trong Scala 2.8.0 và thường được sử dụng với mẫu lớp được gọi là kiểu mẫu , một mẫu mã mô phỏng chức năng được cung cấp bởi các lớp loại Haskell, mặc dù theo cách thức dài dòng hơn.

Mặc dù chế độ xem bị ràng buộc có thể được sử dụng với các loại đơn giản (ví dụ A <% String:), một bối cảnh bị ràng buộc yêu cầu một loại tham số hóa , chẳng hạn như Ordered[A]ở trên, nhưng không giống như String.

Một bối cảnh bị ràng buộc mô tả một giá trị ngầm định , thay vì xem chuyển đổi ngầm định của ràng buộc . Nó được sử dụng để tuyên bố rằng đối với một số loại A, có một giá trị ngầm định của loại B[A]có sẵn. Cú pháp như sau:

def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]

Điều này gây nhầm lẫn hơn so với chế độ xem bị ràng buộc bởi vì nó không rõ ràng ngay lập tức làm thế nào để sử dụng nó. Ví dụ phổ biến về việc sử dụng trong Scala là:

def f[A : ClassManifest](n: Int) = new Array[A](n)

Một Arraykhởi tạo trên một kiểu tham số hóa đòi hỏi ClassManifestphải có sẵn, vì những lý do phức tạp liên quan đến việc xóa kiểu và bản chất không xóa của mảng.

Một ví dụ rất phổ biến khác trong thư viện phức tạp hơn một chút:

def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)

Ở đây, implicitlyđược sử dụng để truy xuất giá trị ngầm định mà chúng ta muốn, một kiểu Ordering[A], lớp nào định nghĩa phương thức compare(a: A, b: A): Int.

Chúng ta sẽ thấy một cách khác để làm điều này dưới đây.

View Bound và Bối cảnh được triển khai như thế nào?

Không có gì đáng ngạc nhiên khi cả giới hạn xem và giới hạn bối cảnh được triển khai với các tham số ngầm định, được đưa ra định nghĩa của chúng. Trên thực tế, cú pháp tôi chỉ ra là đường cú pháp cho những gì thực sự xảy ra. Xem bên dưới cách họ khử đường:

def f[A <% B](a: A) = a.bMethod
def f[A](a: A)(implicit ev: A => B) = a.bMethod

def g[A : B](a: A) = h(a)
def g[A](a: A)(implicit ev: B[A]) = h(a)

Vì vậy, một cách tự nhiên, người ta có thể viết chúng theo cú pháp đầy đủ của chúng, đặc biệt hữu ích cho giới hạn ngữ cảnh:

def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)

View Bound được sử dụng để làm gì?

Giới hạn lượt xem được sử dụng chủ yếu để tận dụng mô hình thư viện của tôi , thông qua đó một phương thức "thêm" vào một lớp hiện có, trong trường hợp bạn muốn trả về kiểu ban đầu bằng cách nào đó. Nếu bạn không cần phải trả về loại đó theo bất kỳ cách nào, thì bạn không cần một chế độ xem bị ràng buộc.

Ví dụ cổ điển về việc sử dụng ràng buộc xem là xử lý Ordered. Lưu ý rằng Intkhông phải là Ordered, ví dụ, mặc dù có một chuyển đổi ngầm. Ví dụ được đưa ra trước đây cần một khung nhìn bị ràng buộc bởi vì nó trả về kiểu không được chuyển đổi:

def f[A <% Ordered[A]](a: A, b: A): A = if (a < b) a else b

Ví dụ này sẽ không hoạt động mà không có giới hạn xem. Tuy nhiên, nếu tôi phải trả về loại khác, thì tôi không cần phải xem chế độ xem nữa:

def f[A](a: Ordered[A], b: A): Boolean = a < b

Việc chuyển đổi ở đây (nếu cần) xảy ra trước khi tôi chuyển tham số cho f, vì vậy fkhông cần biết về nó.

Ngoài ra Ordered, cách sử dụng phổ biến nhất từ ​​thư viện là xử lý StringArray, đó là các lớp Java, giống như chúng là các bộ sưu tập Scala. Ví dụ:

def f[CC <% Traversable[_]](a: CC, b: CC): CC = if (a.size < b.size) a else b

Nếu một người cố gắng làm điều này mà không có giới hạn lượt xem, kiểu trả về của a Stringsẽ là WrappedString(Scala 2.8) và tương tự cho Array.

Điều tương tự xảy ra ngay cả khi loại chỉ được sử dụng làm tham số loại của loại trả về:

def f[A <% Ordered[A]](xs: A*): Seq[A] = xs.toSeq.sorted

Bối cảnh được sử dụng để làm gì?

Giới hạn bối cảnh chủ yếu được sử dụng trong cái được gọi là mẫu typeclass , như một tham chiếu đến các lớp loại của Haskell. Về cơ bản, mẫu này thực hiện thay thế cho kế thừa bằng cách cung cấp chức năng thông qua một loại mẫu bộ điều hợp ngầm.

Ví dụ kinh điển là Scala 2.8 Ordering, thay thế Orderedtrong thư viện của Scala. Cách sử dụng là:

def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b

Mặc dù bạn sẽ thường thấy rằng được viết như thế này:

def f[A](a: A, b: A)(implicit ord: Ordering[A]) = {
    import ord.mkOrderingOps
    if (a < b) a else b
}

Việc tận dụng một số chuyển đổi ngầm bên trong Orderingcho phép kiểu toán tử truyền thống. Một ví dụ khác trong Scala 2.8 là Numeric:

def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b)

Một ví dụ phức tạp hơn là việc sử dụng bộ sưu tập mới CanBuildFrom, nhưng đã có một câu trả lời rất dài về điều đó, vì vậy tôi sẽ tránh nó ở đây. Và, như đã đề cập trước đây, có ClassManifestcách sử dụng, được yêu cầu để khởi tạo các mảng mới mà không cần các loại cụ thể.

Bối cảnh bị ràng buộc với mẫu kiểu chữ có nhiều khả năng được sử dụng bởi các lớp của chính bạn, vì chúng cho phép phân tách các mối quan tâm, trong khi các giới hạn có thể tránh được trong mã của bạn bằng thiết kế tốt (nó được sử dụng chủ yếu để đi xung quanh thiết kế của người khác ).

Mặc dù điều đó đã có thể trong một thời gian dài, việc sử dụng các giới hạn bối cảnh đã thực sự phát huy trong năm 2010, và hiện được tìm thấy ở một mức độ nào đó trong hầu hết các thư viện và khung quan trọng nhất của Scala. Tuy nhiên, ví dụ cực đoan nhất về cách sử dụng của nó là thư viện Scalaz, nơi mang lại rất nhiều sức mạnh của Haskell cho Scala. Tôi khuyên bạn nên đọc lên các mẫu typeclass để làm quen với tất cả các cách mà nó có thể được sử dụng.

BIÊN TẬP

Các câu hỏi liên quan:


9
Cảm ơn bạn rất nhiều. Tôi biết điều này đã được trả lời trước đó và có lẽ tôi đã không đọc kỹ sau đó, nhưng lời giải thích của bạn ở đây là điều rõ ràng nhất tôi từng thấy. Vì vậy, cảm ơn bạn một lần nữa.
chrsan

3
@chrsan Tôi đã thêm hai phần nữa, đi sâu vào chi tiết hơn về nơi một người sử dụng mỗi phần.
Daniel C. Sobral

2
Tôi nghĩ rằng đây là một lời giải thích tuyệt vời. Tôi muốn dịch cái này cho blog tiếng Đức của tôi (dgronau.wordpress.com) nếu nó ổn với bạn.
Landei

3
Đây là lời giải thích tốt nhất và toàn diện nhất về chủ đề này mà tôi đã tìm thấy cho đến nay. Cảm ơn bạn rất nhiều thực sự!
fotNelton

2
Vậy thì, khi nào cuốn sách Scala của bạn được phát hành, và tôi có thể mua nó ở đâu :)
wfbarksdale
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.