Một trong những tính năng mới của Scala 2.8 là giới hạn ngữ cảnh. Một ngữ cảnh ràng buộc là gì và nó hữu ích ở đâu?
Tất nhiên tôi đã tìm kiếm trước (và tìm ví dụ này ) nhưng tôi không thể tìm thấy bất kỳ thông tin chi tiết và rõ ràng nào.
Một trong những tính năng mới của Scala 2.8 là giới hạn ngữ cảnh. Một ngữ cảnh ràng buộc là gì và nó hữu ích ở đâu?
Tất nhiên tôi đã tìm kiếm trước (và tìm ví dụ này ) nhưng tôi không thể tìm thấy bất kỳ thông tin chi tiết và rõ ràng nào.
Câu trả lời:
Bạn đã tìm thấy bài viết này ? Nó bao gồm tính năng ràng buộc ngữ cảnh mới, trong bối cảnh cải tiến mảng.
Nói chung, một tham số kiểu có giới hạn ngữ cảnh có dạng[T: Bound]
; nó được mở rộng thành tham số kiểu thuần túy T
cùng với tham số ngầm định của kiểuBound[T]
.
Hãy xem xét phương pháp tabulate
tạo thành một mảng từ kết quả của việc áp dụng một hàm f cho trước trên một dải số từ 0 cho đến một độ dài nhất định. Lên đến Scala 2.7, bảng có thể được viết như sau:
def tabulate[T](len: Int, f: Int => T) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
Trong Scala 2.8, điều này không còn khả thi nữa, vì thông tin thời gian chạy là cần thiết để tạo ra biểu diễn phù hợp Array[T]
. Người ta cần cung cấp thông tin này bằng cách chuyển một ClassManifest[T]
vào phương thức dưới dạng một tham số ngầm định:
def tabulate[T](len: Int, f: Int => T)(implicit m: ClassManifest[T]) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
Dưới dạng viết tắt, thay vào đó, một ràng buộc ngữ cảnh có thể được sử dụng trên tham số kiểu T
, đưa ra:
def tabulate[T: ClassManifest](len: Int, f: Int => T) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
Câu trả lời của Robert bao gồm các chi tiết kỹ thuật của Context Bounds. Tôi sẽ cung cấp cho bạn cách giải thích của tôi về ý nghĩa của chúng.
Trong Scala, một View Bound ( A <% B
) nắm bắt khái niệm 'có thể được xem là' (trong khi một giới hạn trên <:
nắm bắt khái niệm 'là một'). Một ngữ cảnh bị ràng buộc ( A : C
) cho biết 'có một' về một loại. Bạn có thể đọc các ví dụ về tệp kê khai là " T
có một Manifest
". Ví dụ bạn liên kết đến about Ordered
vs Ordering
minh họa sự khác biệt. Một phương pháp
def example[T <% Ordered[T]](param: T)
nói rằng tham số có thể được xem như là một Ordered
. So sánh với
def example[T : Ordering](param: T)
cho biết rằng tham số có một liên kết Ordering
.
Về mặt sử dụng, phải mất một thời gian để các quy ước được thiết lập, nhưng giới hạn ngữ cảnh được ưu tiên hơn giới hạn chế độ xem ( giới hạn chế độ xem hiện không được dùng nữa ). Một gợi ý là ràng buộc ngữ cảnh được ưu tiên hơn khi bạn cần chuyển một định nghĩa ngầm định từ phạm vi này sang phạm vi khác mà không cần tham chiếu trực tiếp đến nó (điều này chắc chắn là trường hợp ClassManifest
được sử dụng để tạo một mảng).
Một cách khác để suy nghĩ về giới hạn lượt xem và giới hạn ngữ cảnh là giới hạn đầu tiên chuyển các chuyển đổi ngầm khỏi phạm vi của người gọi. Thứ hai chuyển các đối tượng ngầm khỏi phạm vi của người gọi.
has a
Ý nghĩa hơn đối với tôi]
(Đây là ghi chú trong ngoặc đơn. Hãy đọc và hiểu các câu trả lời khác trước.)
Context Bounds thực sự khái quát hóa View Bounds.
Vì vậy, với mã này được biểu thị bằng một Giới hạn Chế độ xem:
scala> implicit def int2str(i: Int): String = i.toString
int2str: (i: Int)String
scala> def f1[T <% String](t: T) = 0
f1: [T](t: T)(implicit evidence$1: (T) => String)Int
Điều này cũng có thể được thể hiện bằng Ranh giới ngữ cảnh, với sự trợ giúp của bí danh kiểu đại diện cho các hàm từ kiểu này F
sang kiểu khác T
.
scala> trait To[T] { type From[F] = F => T }
defined trait To
scala> def f2[T : To[String]#From](t: T) = 0
f2: [T](t: T)(implicit evidence$1: (T) => java.lang.String)Int
scala> f2(1)
res1: Int = 0
Một ràng buộc ngữ cảnh phải được sử dụng với một phương thức khởi tạo kiểu thuộc loại * => *
. Tuy nhiên, hàm tạo kiểu Function1
là loại (*, *) => *
. Việc sử dụng bí danh kiểu áp dụng một phần tham số kiểu thứ hai với kiểuString
, tạo ra một phương thức khởi tạo kiểu thuộc loại chính xác để sử dụng làm giới hạn ngữ cảnh.
Có một đề xuất cho phép bạn thể hiện trực tiếp các kiểu được áp dụng một phần trong Scala mà không cần sử dụng bí danh kiểu bên trong một đặc điểm. Sau đó bạn có thể viết:
def f3[T : [X](X => String)](t: T) = 0
From
của kiểu To[String]
. Chúng tôi không cung cấp một đối số kiểu From
, vì vậy chúng tôi đề cập đến phương thức tạo kiểu, không phải là một kiểu. Phương thức khởi tạo kiểu này thuộc loại phù hợp để được sử dụng làm ràng buộc ngữ cảnh - * -> *
. Điều này giới hạn tham số kiểu T
bằng cách yêu cầu một tham số ngầm định của kiểu To[String]#From[T]
. Mở rộng loại bí danh, và thì đấy, bạn còn lại Function1[String, T]
.
Đây là một ghi chú ngoặc đơn khác.
Như Ben đã chỉ ra , một ràng buộc ngữ cảnh đại diện cho một ràng buộc "có-một" giữa một tham số kiểu và một lớp kiểu. Nói cách khác, nó đại diện cho một ràng buộc rằng tồn tại một giá trị ngầm định của một lớp kiểu cụ thể.
Khi sử dụng một ngữ cảnh bị ràng buộc, người ta thường cần hiển thị giá trị tiềm ẩn đó. Ví dụ, với ràng buộc T : Ordering
, người ta thường sẽ cần thể hiện của Ordering[T]
nó thỏa mãn ràng buộc. Như đã trình bày ở đây , có thể truy cập giá trị ngầm định bằng cách sử dụng implicitly
phương thức hoặc context
phương pháp hữu ích hơn một chút :
def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) =
xs zip ys map { t => implicitly[Numeric[T]].times(t._1, t._2) }
hoặc là
def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) =
xs zip ys map { t => context[T]().times(t._1, t._2) }