2012-04-29 29 views
241

Tôi đã làm theo cách của mình thông qua các Scala play framework hướng dẫn và tôi đã xem qua đoạn mã này trong đó có tôi bối rối:Hiểu ngầm tại Scala

def newTask = Action { implicit request => 
taskForm.bindFromRequest.fold(
     errors => BadRequest(views.html.index(Task.all(), errors)), 
     label => { 
      Task.create(label) 
      Redirect(routes.Application.tasks()) 
     } 
) 
} 

Vì vậy, tôi quyết định để điều tra và đã xem qua this post.

Tôi vẫn chưa hiểu.

sự khác biệt giữa điều này là gì:

implicit def double2Int(d : Double) : Int = d.toInt 

def double2IntNonImplicit(d : Double) : Int = d.toInt 

khác so với thực tế rõ ràng họ có tên phương pháp khác nhau.

Khi nào tôi nên sử dụng implicit và tại sao?

Trả lời

313

Tôi sẽ giải thích các trường hợp sử dụng chính của implicit dưới đây, nhưng để biết thêm chi tiết, xem relevant chapter of Programming in Scala.

thông số Implicit

Trận chung kết danh sách tham số trên một phương pháp có thể được đánh dấu implicit, có nghĩa là giá trị này sẽ được lấy từ bối cảnh trong đó chúng được gọi. Nếu không có giá trị tiềm ẩn của đúng loại trong phạm vi, nó sẽ không biên dịch. Vì giá trị tiềm ẩn phải giải quyết cho một giá trị duy nhất và để tránh xung đột, nên tạo loại cụ thể cho mục đích của nó, ví dụ: không yêu cầu các phương pháp của bạn để tìm kiếm một ẩn số Int!

dụ:

// probably in a library 
class Prefixer(val prefix: String) 
def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s 

    // then probably in your application 
implicit val myImplicitPrefixer = new Prefixer("***") 
addPrefix("abc") // returns "***abc" 

chuyển đổi Implicit

Khi trình biên dịch tìm thấy một biểu hiện của loại sai cho bối cảnh, nó sẽ tìm kiếm một giá trị tiềm ẩn Function của một loại mà sẽ cho phép nó để đánh máy. Vì vậy, nếu yêu cầu A và nó tìm thấy một B, nó sẽ tìm kiếm một giá trị tiềm ẩn thuộc loại B => A trong phạm vi (nó cũng kiểm tra một số địa điểm khác như trong các đối tượng đồng hành BA, nếu chúng tồn tại). Vì def s có thể được "mở rộng eta" thành các đối tượng Function, một implicit def xyz(arg: B): A cũng sẽ hoạt động.

Vì vậy, sự khác biệt giữa các phương pháp của bạn là phương pháp được đánh dấu implicit sẽ được trình biên dịch đưa vào cho bạn khi một số Double được tìm thấy nhưng yêu cầu Int.

implicit def doubleToInt(d: Double) = d.toInt 
val x: Int = 42.0 

sẽ làm việc giống như

def doubleToInt(d: Double) = d.toInt 
val x: Int = doubleToInt(42.0) 

Trong thứ hai chúng tôi đã chèn chuyển đổi bằng tay; trong trình biên dịch đầu tiên cũng tự động làm như vậy. Chuyển đổi là bắt buộc vì chú thích loại ở phía bên tay trái.


Về đoạn đầu tiên của bạn từ Play:

Hoạt động được giải thích trên this page từ tài liệu Phát (xem thêm API docs). Bạn đang sử dụng

apply(block: (Request[AnyContent]) ⇒ Result): Action[AnyContent] 

đối tượng Action (là đồng hành với đặc điểm cùng tên).

Vì vậy, chúng ta cần phải cung cấp một chức năng như là đối số, mà có thể được viết như một chữ theo hình thức

request => ... 

Trong một hàm nghĩa đen, phần trước => là một tuyên bố giá trị, và có thể được đánh dấu implicit nếu bạn muốn, giống như trong bất kỳ tuyên bố khác val nào. Ở đây, requestkhông phải được đánh dấu implicit để làm điều này để kiểm tra, nhưng bằng cách làm như vậy nó sẽ là sẵn có dưới dạng giá trị ẩn cho bất kỳ phương pháp nào có thể cần nó trong hàm (và tất nhiên, nó có thể cũng được sử dụng một cách rõ ràng). Trong trường hợp cụ thể này, điều này đã được thực hiện vì phương thức bindFromRequest trên lớp Form yêu cầu đối số ngụ ý Request.

+12

Cảm ơn bạn đã phản hồi. Liên kết cho chương 21 thực sự tuyệt vời. Cảm kích điều đó. – Clive

+13

Chỉ cần thêm phần này, video sau đây sẽ giải thích rõ ràng về các mối liên hệ với một số tính năng khác của scala http://www.youtube.com/watch?v=IobLWVuD-CQ – Shakti

3

Tại sao và khi nào bạn nên đánh dấu request tham số như implicit:

Một số phương pháp mà bạn sẽ sử dụng trong cơ thể của hành động của bạn có một danh sách tham số ngầm như, ví dụ, định nghĩa Form.scala một phương pháp:

def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = { ... } 

bạn không nhất thiết nhận thấy điều này như bạn sẽ chỉ cần gọi myForm.bindFromRequest() bạn không cần phải cung cấp các luận cứ ngầm một cách rõ ràng. Không, bạn để lại trình biên dịch để tìm bất kỳ đối tượng ứng cử viên hợp lệ nào để vượt qua mỗi khi gặp phải một cuộc gọi phương thức yêu cầu một phiên bản của yêu cầu. Vì bạn do có sẵn một yêu cầu, tất cả những gì bạn cần làm là đánh dấu nó là implicit.

Bạn rõ ràng đánh dấu là có sẵn để sử dụng ẩn sử dụng.

Bạn gợi ý trình biên dịch là "OK" để sử dụng đối tượng yêu cầu được gửi bởi khung chơi (mà chúng tôi đã đặt tên là "yêu cầu" nhưng có thể chỉ sử dụng "r" hoặc "req") ở bất cứ đâu, " trên sly ".

myForm.bindFromRequest() 

xem? nó không có ở đó, nhưng nó là ở đó!

Nó chỉ xảy ra mà không cần phải của bạn để khe cắm nó trong tay trong mọi vị trí cần thiết (nhưng bạn thể vượt qua nó một cách rõ ràng, nếu bạn rất muốn, không có vấn đề nếu nó được đánh dấu implicit hay không):

myForm.bindFromRequest()(request) 

Nếu không đánh dấu nó là ngầm định, bạn sẽ phải làm như trên. Đánh dấu nó là tiềm ẩn bạn không phải làm.

Khi bạn có nên đánh dấu yêu cầu là implicit không? Bạn chỉ thực sự cần nếu bạn đang sử dụng các phương thức khai báo danh sách tham số ẩn mong đợi một phiên bản của Yêu cầu. Nhưng để đơn giản, bạn chỉ cần có thói quen đánh dấu yêu cầu implicitluôn là. Bằng cách đó bạn chỉ có thể viết mã terse đẹp.

19

CẢNH BÁO: chứa mỉa mai một cách thận trọng! YMMV ...

Luigi's answer hoàn chỉnh và chính xác. Điều này chỉ là để mở rộng nó một chút với một ví dụ về cách bạn có thể vinh quang lạm dụng implicits, vì nó xảy ra khá thường xuyên trong các dự án Scala. Thực tế thường xuyên, bạn thậm chí có thể tìm thấy nó ở một trong các hướng dẫn "Thực tiễn tốt nhất".

object HelloWorld { 
    case class Text(content: String) 
    case class Prefix(text: String) 

    implicit def String2Text(content: String)(implicit prefix: Prefix) = { 
    Text(prefix.text + " " + content) 
    } 

    def printText(text: Text): Unit = { 
    println(text.content) 
    } 

    def main(args: Array[String]): Unit = { 
    printText("World!") 
    } 

    // Best to hide this line somewhere below a pile of completely unrelated code. 
    // Better yet, import its package from another distant place. 
    implicit val prefixLOL = Prefix("Hello") 
} 
+0

Haha. Khiếu hài hước. – Det

0

Ngoài ra, trong trường hợp trên cần có only one chức năng tiềm ẩn có loại là double => Int. Nếu không, trình biên dịch sẽ bị nhầm lẫn và sẽ không biên dịch đúng cách.

//this won't compile 

implicit def doubleToInt(d: Double) = d.toInt 
implicit def doubleToIntSecond(d: Double) = d.toInt 
val x: Int = 42.0 
Các vấn đề liên quan