Số nhận dạng Scala là gì ngầm ngầm là gì?


169

Tôi đã thấy một hàm có tên implicitlyđược sử dụng trong các ví dụ Scala. Nó là gì và làm thế nào để dùng nó?

Ví dụ ở đây :

scala> sealed trait Foo[T] { def apply(list : List[T]) : Unit }; object Foo {
     |                         implicit def stringImpl = new Foo[String] {
     |                             def apply(list : List[String]) = println("String")
     |                         }
     |                         implicit def intImpl = new Foo[Int] {
     |                             def apply(list : List[Int]) =  println("Int")
     |                         }
     |                     } ; def foo[A : Foo](x : List[A]) = implicitly[Foo[A]].apply(x)
defined trait Foo
defined module Foo
foo: [A](x: List[A])(implicit evidence$1: Foo[A])Unit

scala> foo(1)
<console>:8: error: type mismatch;
 found   : Int(1)
 required: List[?]
       foo(1)
           ^
scala> foo(List(1,2,3))
Int
scala> foo(List("a","b","c"))
String
scala> foo(List(1.0))
<console>:8: error: could not find implicit value for evidence parameter of type
 Foo[Double]
       foo(List(1.0))
          ^

Lưu ý rằng chúng ta phải viết implicitly[Foo[A]].apply(x)vì trình biên dịch nghĩ rằng điều đó implicitly[Foo[A]](x)có nghĩa là chúng ta gọi implicitlybằng các tham số.

Xem thêm Cách điều tra các đối tượng / loại / v.v. từ Scala REPL? Scala tìm kiếm những ẩn ý ở đâu?

Câu trả lời:


205

Dưới đây là một vài lý do để sử dụng phương pháp đơn giản thú vị implicitly.

Để hiểu / khắc phục sự cố Chế độ xem ngầm

Chế độ xem ngầm có thể được kích hoạt khi tiền tố của lựa chọn (ví dụ: xem xét, the.prefix.selection(args)không chứa thành viên selectioncó thể áp dụng cho args(ngay cả sau khi cố gắng chuyển đổi argsvới Chế độ xem ngầm). Trong trường hợp này, trình biên dịch tìm kiếm thành viên ẩn trong phạm vi hiện tại hoặc kèm theo, được kế thừa hoặc được nhập, là các Hàm từ loại đó the.prefixsang loại có selectioncác phương thức ngầm định hoặc tương đương.

scala> 1.min(2) // Int doesn't have min defined, where did that come from?                                   
res21: Int = 1

scala> implicitly[Int => { def min(i: Int): Any }]
res22: (Int) => AnyRef{def min(i: Int): Any} = <function1>

scala> res22(1) // 
res23: AnyRef{def min(i: Int): Int} = 1

scala> .getClass
res24: java.lang.Class[_] = class scala.runtime.RichInt

Chế độ xem ngầm định cũng có thể được kích hoạt khi một biểu thức không phù hợp với Loại dự kiến, như dưới đây:

scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1

Ở đây trình biên dịch tìm hàm này:

scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>

Truy cập một tham số ngầm định được giới thiệu bởi giới hạn bối cảnh

Các tham số ngầm định được cho là một tính năng quan trọng hơn của Scala so với Chế độ xem ngầm định. Họ hỗ trợ các mẫu lớp. Thư viện tiêu chuẩn sử dụng điều này ở một vài nơi - xem scala.Orderingvà cách sử dụng nó SeqLike#sorted. Các tham số ngầm định cũng được sử dụng để truyền các bảng kê khai và các CanBuildFromthể hiện của Array .

Scala 2.8 cho phép một cú pháp tốc ký cho các tham số ngầm, được gọi là Giới hạn bối cảnh. Tóm lại, một phương thức có tham số kiểu Ayêu cầu tham số ngầm định kiểu M[A]:

def foo[A](implicit ma: M[A])

có thể được viết lại thành:

def foo[A: M]

Nhưng ý nghĩa của việc truyền tham số ngầm nhưng không đặt tên cho nó là gì? Làm thế nào điều này có thể hữu ích khi thực hiện phương pháp foo?

Thông thường, tham số ngầm không cần phải được tham chiếu trực tiếp, nó sẽ được chuyển qua như là một đối số ngầm cho một phương thức khác được gọi. Nếu cần thiết, bạn vẫn có thể giữ lại chữ ký phương thức ngắn gọn với Giới hạn ngữ cảnh và gọi implicitlyđể cụ thể hóa giá trị:

def foo[A: M] = {
   val ma = implicitly[M[A]]
}

Truyền một tập hợp con các tham số ngầm

Giả sử bạn đang gọi một phương thức in đẹp một người, sử dụng cách tiếp cận dựa trên lớp loại:

trait Show[T] { def show(t: T): String }
object Show {
  implicit def IntShow: Show[Int] = new Show[Int] { def show(i: Int) = i.toString }
  implicit def StringShow: Show[String] = new Show[String] { def show(s: String) = s }

  def ShoutyStringShow: Show[String] = new Show[String] { def show(s: String) = s.toUpperCase }
}

case class Person(name: String, age: Int)
object Person {
  implicit def PersonShow(implicit si: Show[Int], ss: Show[String]): Show[Person] = new Show[Person] {
    def show(p: Person) = "Person(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")"
  }
}

val p = Person("bob", 25)
implicitly[Show[Person]].show(p)

Điều gì xảy ra nếu chúng ta muốn thay đổi cách mà tên là đầu ra? Chúng tôi có thể gọi một cách rõ ràng PersonShow, vượt qua một cách rõ ràng Show[String], nhưng chúng tôi muốn trình biên dịch vượt qua Show[Int].

Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)

2
scala> 1.min (2) res0: Int = 1 Trong Scala 2.10.3 Tôi gặp lỗi: scala> ngầm [Int => {def min (i: Int): Any}] <console>: 8: error: Không có chế độ xem ngầm định có sẵn từ Int => AnyRef {def min (i: Int): Any}. ngầm [Int => {def min (i: Int): Any}]
jhegedus 17/214

Câu trả lời này sẽ được cập nhật cho phiên bản mới nhất.
emeth

1
ngầm định [Int => AnyVal {def min (i: Int): Int}] sẽ hoạt động. Nên sửa trong câu trả lời.
Malkaviano

211

Implicitlycó sẵn trong Scala 2.8 và được định nghĩa trong Predef là:

def implicitly[T](implicit e: T): T = e

Nó thường được sử dụng để kiểm tra xem giá trị ngầm định của loại Tcó sẵn hay không và trả về nếu đó là trường hợp.

Ví dụ đơn giản từ bản trình bày của retronym :

scala> implicit val a = "test" // define an implicit value of type String
a: java.lang.String = test
scala> val b = implicitly[String] // search for an implicit value of type String and assign it to b
b: String = test
scala> val c = implicitly[Int] // search for an implicit value of type Int and assign it to c
<console>:6: error: could not find implicit value for parameter e: Int
       val c = implicitly[Int]
                         ^

6
Phương pháp không kiểm tra chính xác; nó dường như gây ra lỗi biên dịch nếu không có giá trị ngầm định và nếu có, dường như để truy xuất nó. Bạn có thể cung cấp thêm một số bối cảnh về lý do tại sao tôi muốn sử dụng nó không?
davetron5000

17
implicitly[Ordering[(Int, String)]].compare( (1, "b"), (1, "a") ), đặc biệt là để truy xuất một tham số ngầm được giới thiệu bởi Giới hạn bối cảnh:def foo[A: Ordering](a1: A, a2: A) = implicitly[Ordering[A]].compare(a1, a2)
retronym

1
Để xem thảo luận của retronym trong liên kết video ở trên, bỏ qua điểm 13:50.
hỗn loạn3quilibrium

-2

Câu trả lời "dạy bạn câu cá" là sử dụng chỉ số thành viên bảng chữ cái hiện có sẵn trong các đêm của Scaladoc . Các chữ cái (và #, đối với các tên không phải là chữ cái) ở đầu ngăn gói / lớp là các liên kết đến chỉ mục cho các tên thành viên bắt đầu bằng chữ cái đó (trên tất cả các lớp). Nếu bạn chọn I, ví dụ: bạn sẽ tìm thấy implicitlymục nhập với một lần xuất hiện, trong Predefđó bạn có thể truy cập từ liên kết ở đó.


46
Tất nhiên, những scaladocs không nói gì về ngầm, do đó hầu như không được coi là tài liệu. Làm thế nào một người nào đó sẽ tìm ra phương pháp đó làm gì từ những tài liệu đó một mình? Tôi cảm thấy thường xuyên buông xuống bởi tài liệu Scala. Hành vi của các phương thức như ngầm là không rõ ràng, và tài liệu về chúng hầu như không tốt hơn so với không có. Cảm ơn chúa vì Stack Overflow. / kết thúc rant
Jeff


4
Các loại chữ ký tài liệu này khá tốt.
retronym

21
implicitdường như là một tính năng ngôn ngữ quan trọng trong Scala, và chắc chắn xứng đáng để giải thích thích hợp. Nghĩ rằng các tài liệu chỉ nêu chi tiết về số lượng chữ ký có vẻ giống như sự tự hài lòng về trí tuệ, hơn là một câu trả lời chân thực. Xem các câu hỏi cụ thể của OP - nó là gì và nó được sử dụng như thế nào? Không được trả lời bởi điều này, hoặc trong các tài liệu hàng đêm mà bạn thậm chí không cung cấp một liên kết thực sự đến. scala-lang.org/files/archive/nightly/docs/l Library / trộm Điều này không dạy gì cả. Xem Niklaus Wirth hoặc Turbo Pascal để biết ví dụ về các tài liệu chính hãng. -1
Thomas W

3
implicitimplicitlycó liên quan, nhưng khá khác biệt. Các implicittừ khóa là một phần của ngôn ngữ. implicitlyđược định nghĩa bằng mã Scala đơn giản trong thư viện chuẩn. Vì các tài liệu trực tuyến bao gồm các liên kết nguồn, tôi tin rằng tốt nhất vẫn là giới thiệu người hỏi đến các tài liệu đó và nguồn được liên kết.
Randall Schulz
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.