Was ist die Scala-Kennung "implizit"?

169

Ich habe eine Funktion namens implicitlyScala gesehen, die in Scala-Beispielen verwendet wird. Was ist das und wie wird es verwendet?

Beispiel hier :

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

Beachten Sie, dass wir schreiben müssen, implicitly[Foo[A]].apply(x)da der Compiler denkt, dass dies implicitly[Foo[A]](x)bedeutet, dass wir implicitlymit Parametern aufrufen .

Siehe auch Untersuchen von Objekten / Typen / etc. von Scala REPL? und wo sucht Scala nach Implikaten?

oluies
quelle

Antworten:

205

Hier sind einige Gründe, die wunderbar einfache Methode anzuwenden implicitly.

Implizite Ansichten verstehen / Fehler beheben

Eine implizite Ansicht kann ausgelöst werden, wenn das Präfix einer Auswahl (z. B. the.prefix.selection(args)enthält es kein Element selection, auf das es anwendbar ist args(auch nach dem Versuch, argsmit impliziten Ansichten zu konvertieren ). In diesem Fall sucht der Compiler nach impliziten Elementen, die lokal definiert sind In den aktuellen oder umschließenden Bereichen, geerbt oder importiert, handelt es sich entweder um Funktionen vom Typ dieses the.prefixTyps zu einem Typ mit selectiondefinierten oder äquivalenten impliziten Methoden.

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

Implizite Ansichten können auch ausgelöst werden, wenn ein Ausdruck nicht dem erwarteten Typ entspricht (siehe unten):

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

Hier sucht der Compiler nach dieser Funktion:

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

Zugriff auf einen impliziten Parameter, der durch eine Kontextbindung eingeführt wird

Implizite Parameter sind wohl ein wichtigeres Merkmal von Scala als implizite Ansichten. Sie unterstützen das Typklassenmuster. Die Standardbibliothek verwendet dies an einigen Stellen - sehen Sie, scala.Orderingwie es verwendet wird SeqLike#sorted. Implizite Parameter werden auch zum Übergeben von Array-Manifesten und CanBuildFrom-Instanzen verwendet.

Scala 2.8 ermöglicht eine Kurzsyntax für implizite Parameter, die als Kontextgrenzen bezeichnet wird. Kurz gesagt, eine Methode mit einem Typparameter A, für den ein impliziter Parameter vom Typ erforderlich ist M[A]:

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

kann umgeschrieben werden als:

def foo[A: M]

Aber was bringt es, den impliziten Parameter zu übergeben, aber nicht zu benennen? Wie kann dies bei der Implementierung der Methode hilfreich sein foo?

Oft muss auf den impliziten Parameter nicht direkt verwiesen werden, sondern er wird als implizites Argument zu einer anderen aufgerufenen Methode getunnelt. Wenn es benötigt wird, können Sie die knappe Methodensignatur mit dem Context Bound beibehalten und aufrufen, implicitlyum den Wert zu materialisieren:

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

Eine Teilmenge impliziter Parameter explizit übergeben

Angenommen, Sie rufen eine Methode auf, die eine Person mit einem typklassenbasierten Ansatz hübsch druckt:

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)

Was ist, wenn wir die Art und Weise ändern möchten, in der der Name ausgegeben wird? Wir können explizit PersonShoweine Alternative aufrufen , explizit übergeben Show[String], aber wir möchten, dass der Compiler die übergibt Show[Int].

Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)
Retronym
quelle
2
scala> 1.min (2) res0: Int = 1 In Scala 2.10.3 wird eine Fehlermeldung angezeigt: scala> implizit [Int => {def min (i: Int): Beliebig}] <Konsole>: 8: Fehler: Keine implizite Ansicht verfügbar von Int => AnyRef {def min (i: Int): Any}. implizit [Int => {def min (i: Int): Any}]
jhegedus
Diese Antwort wird für die neueste Version aktualisiert.
Emeth
1
implizit funktioniert [Int => AnyVal {def min (i: Int): Int}]. Sollte in der Antwort festgelegt werden.
Malkaviano
211

Implicitlyist in Scala 2.8 verfügbar und in Predef definiert als:

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

Es wird häufig verwendet, umT zu überprüfen, ob ein impliziter Wert vom Typ verfügbar ist, und ihn zurückzugeben, wenn dies der Fall ist.

Einfaches Beispiel aus der Präsentation von 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]
                         ^
oluies
quelle
6
Die Methode prüft nicht genau; Es scheint einen Kompilierungsfehler zu verursachen, wenn kein impliziter Wert verfügbar ist, und wenn vorhanden, scheint er ihn abzurufen. Können Sie einen weiteren Kontext angeben, warum ich dies jemals verwenden möchte?
davetron5000
17
implicitly[Ordering[(Int, String)]].compare( (1, "b"), (1, "a") ), insbesondere um einen impliziten Parameter abzurufen, der durch eine Kontextbindung eingeführt wurde:def foo[A: Ordering](a1: A, a2: A) = implicitly[Ordering[A]].compare(a1, a2)
Retronym
1
Um die Diskussion von retronym im obigen Videolink zu sehen, fahren Sie mit Punkt 13:50 fort.
chaotic3quilibrium
-2

Eine Antwort " Bringen Sie das Fischen bei" besteht darin, den alphabetischen Mitgliederindex zu verwenden, der derzeit in den Scaladoc-Nachthemden verfügbar ist . Die Buchstaben (und die #für nicht alphabetische Namen) oben im Paket- / Klassenbereich sind Links zum Index für Mitgliedsnamen, die mit diesem Buchstaben beginnen (über alle Klassen hinweg). Wenn Sie Iz. B. wählen , finden Sie den implicitlyEintrag mit einem Vorkommen in Predef, den Sie über den dortigen Link besuchen können.

Randall Schulz
quelle
46
Natürlich sagen diese Scaladocs überhaupt nichts implizit aus, so dass dies kaum als Dokumentation gilt. Wie würde jemand allein anhand dieser Dokumente herausfinden, was diese Methode bewirkt? Ich fühle mich routinemäßig von der Scala-Dokumentation enttäuscht. Das Verhalten von Methoden wie implizit ist alles andere als offensichtlich, und die Dokumentation darüber ist kaum besser als nicht vorhanden. Gott sei Dank für Stack Overflow. / Ende schimpfen
Jeff
4
Die Typensignatur dokumentiert dies ziemlich gut.
Retronym
21
implicitscheint ein wichtiges Sprachmerkmal in Scala zu sein und verdient definitiv eine angemessene Erklärung. Der Gedanke, dass Dokumente, in denen nur die Anzahl der Typensignaturen angegeben ist, eher eine intellektuelle Selbstbefriedigung als eine echte Antwort sind. Sehen Sie sich die spezifischen Fragen des OP an - was ist das und wie wird es verwendet? Weder beantwortet noch in den nächtlichen Dokumenten, zu denen Sie nicht einmal einen tatsächlichen Link bereitstellen. scala-lang.org/files/archive/nightly/docs/library/… Dies lehrt nichts. Beispiele für Originaldokumente finden Sie unter Niklaus Wirth oder Turbo Pascal. -1
Thomas W
3
implicitund implicitlysind verwandt, aber ziemlich verschieden. Das implicitSchlüsselwort ist Teil der Sprache. implicitlywird in der Standardbibliothek in einfachem Scala-Code definiert. Da die Online-Dokumente Quelllinks enthalten, ist es meines Erachtens immer noch am besten, Fragesteller auf diese Dokumente und die verknüpfte Quelle zu verweisen.
Randall Schulz