Apa yang dimaksud dengan pengidentifikasi Scala "secara implisit"?


169

Saya telah melihat fungsi bernama implicitlydigunakan dalam contoh Scala. Apa itu dan bagaimana cara menggunakannya?

Contoh di sini :

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))
          ^

Perhatikan bahwa kita harus menulis implicitly[Foo[A]].apply(x)karena kompiler berpikir itu implicitly[Foo[A]](x)berarti kita memanggil implicitlydengan parameter.

Lihat juga Cara menyelidiki objek / tipe / dll. dari Scala REPL? dan Di mana Scala mencari implisit?

Jawaban:


206

Berikut adalah beberapa alasan untuk menggunakan metode sederhana yang menyenangkan implicitly.

Untuk memahami / memecahkan masalah Tampilan Tersirat

Tampilan Tersirat dapat dipicu ketika awalan suatu seleksi (pertimbangkan misalnya, the.prefix.selection(args)tidak mengandung anggota selectionyang berlaku untuk args(bahkan setelah mencoba untuk mengkonversi argsdengan Tampilan Tersirat). Dalam kasus ini, kompiler mencari anggota tersirat, ditentukan secara lokal dalam lingkup saat ini atau melampirkan, diwariskan, atau diimpor, yang merupakan Fungsi dari jenis yang the.prefixke jenis dengan selectionmetode implisit yang ditetapkan, atau setara.

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

Tampilan Tersirat juga dapat dipicu ketika ekspresi tidak sesuai dengan Tipe yang Diharapkan, seperti di bawah ini:

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

Di sini kompiler mencari fungsi ini:

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

Mengakses Parameter Tersirat Diperkenalkan oleh Batas Konteks

Parameter implisit merupakan fitur Scala yang lebih penting daripada Tampilan Implisit. Mereka mendukung pola kelas tipe. Pustaka standar menggunakannya di beberapa tempat - lihat scala.Orderingdan bagaimana menggunakannya SeqLike#sorted. Parameter implisit juga digunakan untuk melewati manifest Array, dan CanBuildFrominstance.

Scala 2.8 memungkinkan sintaks steno untuk parameter implisit, yang disebut Batas Konteks. Secara singkat, metode dengan parameter tipe Ayang memerlukan parameter tipe implisit M[A]:

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

dapat ditulis ulang sebagai:

def foo[A: M]

Tapi apa gunanya melewati parameter implisit tetapi tidak menamainya? Bagaimana ini bisa berguna ketika menerapkan metode ini foo?

Seringkali, parameter implisit tidak perlu dirujuk secara langsung, itu akan diteruskan sebagai argumen implisit ke metode lain yang disebut. Jika diperlukan, Anda masih bisa mempertahankan tanda tangan metode singkat dengan Bound Konteks, dan panggilan implicitlyuntuk mewujudkan nilai:

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

Melewati sebagian parameter implisit secara eksplisit

Misalkan Anda memanggil metode yang cukup mencetak seseorang, menggunakan pendekatan berbasis kelas tipe:

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)

Bagaimana jika kita ingin mengubah cara nama itu dihasilkan? Kami dapat secara eksplisit memanggil PersonShow, secara eksplisit melewati alternatif Show[String], tetapi kami ingin kompiler untuk lulus Show[Int].

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

2
scala> 1.min (2) res0: Int = 1 Di Scala 2.10.3 Saya mendapatkan error: scala> secara implisit [Int => {def min (i: Int): Any}] <console>: 8: error: Tidak ada tampilan tersirat yang tersedia dari Int => AnyRef {def min (i: Int): Any}. secara implisit [Int => {def min (i: Int): Any}]
jhegedus

Jawaban ini akan diperbarui untuk versi terbaru.
emeth

1
secara implisit [Int => AnyVal {def min (i: Int): Int}] akan berfungsi. Harus diperbaiki dalam jawabannya.
Malkaviano

212

Implicitlytersedia dalam Scala 2.8 dan didefinisikan dalam Predef sebagai:

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

Biasanya digunakan untuk memeriksa apakah nilai implisit tipe Ttersedia dan mengembalikannya jika memang demikian.

Contoh sederhana dari presentasi 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
Metode ini tidak benar-benar memeriksa; tampaknya menyebabkan kesalahan kompilasi jika tidak ada nilai implisit yang tersedia dan, jika ada, sepertinya mengambilnya. Bisakah Anda memberikan lebih banyak konteks tentang mengapa saya ingin menggunakan ini?
davetron5000

17
implicitly[Ordering[(Int, String)]].compare( (1, "b"), (1, "a") ), khususnya untuk mengambil parameter implisit yang diperkenalkan oleh Context Bound:def foo[A: Ordering](a1: A, a2: A) = implicitly[Ordering[A]].compare(a1, a2)
retronym

1
Untuk melihat diskusi retronym di tautan video di atas, lewati ke titik 13:50.
chaotic3quilibrium

-2

Jawaban "mengajarkan Anda untuk menangkap ikan" adalah dengan menggunakan indeks anggota alfabet yang saat ini tersedia di Scaladoc nightlies . Huruf (dan #, untuk nama non-alfabet) di bagian atas panel paket / kelas adalah tautan ke indeks untuk nama anggota yang dimulai dengan huruf itu (di semua kelas). Jika Anda memilih I, misalnya, Anda akan menemukan implicitlyentri dengan satu kejadian, di Predef, yang dapat Anda kunjungi dari tautan di sana.


46
Tentu saja, scaladocs itu tidak mengatakan apa-apa tentang hal itu secara implisit, sehingga hampir tidak dianggap sebagai dokumentasi. Bagaimana seseorang mengetahui apa yang dilakukan metode itu dari dokumen-dokumen itu saja? Saya merasa dikecewakan secara rutin oleh dokumentasi Scala. Perilaku metode seperti secara implisit jauh dari jelas, dan dokumentasi tentang mereka hampir tidak lebih baik daripada tidak ada. Terima kasih Tuhan untuk Stack Overflow. / akhir kata
Jeff


4
Jenis tanda tangan mendokumentasikan yang satu ini cukup baik.
retronym

21
implicittampaknya menjadi fitur bahasa yang penting di Scala, dan jelas layak untuk penjelasan yang tepat. Berpikir bahwa dokumen yang hanya merinci hitungan jenis tanda tangan tampaknya lebih seperti kepuasan diri intelektual, daripada jawaban yang tulus. Lihat pertanyaan spesifik yang diajukan oleh OP - apa itu, dan bagaimana menggunakannya? Tidak dijawab oleh ini, atau dalam dokumen malam yang Anda bahkan tidak memberikan tautan sebenarnya. scala-lang.org/files/archive/nightly/docs/library/… Ini tidak mengajarkan apa-apa. Lihat Niklaus Wirth atau Turbo Pascal untuk contoh dokumen asli. -1
Thomas W

3
implicitdan implicitlyterkait, tetapi sangat berbeda. Kata implicitkunci adalah bagian dari bahasa. implicitlydidefinisikan dalam kode Scala biasa di perpustakaan standar. Karena dokumen on-line menyertakan tautan sumber, saya yakin masih lebih baik untuk merujuk kuesioner ke dokumen-dokumen tersebut dan sumber yang ditautkan.
Randall Schulz
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.