Qu'est-ce que L'identificateur Scala "implicitement"?
j'ai vu une fonction appelée implicitly
utilisée dans les exemples de Scala. Qu'en est-il et comment est-il utilisé?
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: 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))
^
notez que nous devons écrire implicitly[Foo[A]].apply(x)
puisque le compilateur pense que implicitly[Foo[A]](x)
signifie que nous appelons implicitly
avec des paramètres.
voir Aussi Comment enquêter sur les objets de/types de/etc. de Scala REPL? et D'où vient Scala recherchez implicites?
3 réponses
voici quelques raisons d'utiliser la méthode délicieusement simple implicitly
.
pour comprendre/résoudre les vues implicites
une vue implicite peut être déclenchée lorsque le préfixe d'une sélection (par exemple, the.prefix.selection(args)
ne contient pas un membre selection
qui est applicable à args
(même après avoir essayé de convertir args
avec des vues implicites). Dans ce cas, le compilateur recherche les membres implicites, localement défini dans les portées courantes ou enveloppantes, héritées, ou importées, qui sont soit des fonctions du type de ce the.prefix
à un type avec selection
défini, ou méthodes implicites équivalentes.
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
les vues implicites peuvent aussi être déclenchées lorsqu'une expression n'est pas conforme au Type attendu, comme ci-dessous:
scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1
ici le compilateur cherche cette fonction:
scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>
Accès à un paramètre implicite introduit par un contexte lié
les paramètres implicites sont sans doute une caractéristique plus importante de Scala que les vues implicites. Ils supportent le modèle de classe type. La bibliothèque standard utilise dans quelques endroits, voir scala.Ordering
et comment il est utilisé dans SeqLike#sorted
. Les paramètres implicites sont également utilisés pour passer les manifestes des tableaux et les instances CanBuildFrom
.
Scala 2.8 permet une syntaxe abrégée pour implicite paramètres, appelés limites de contexte. Brièvement, une méthode avec un paramètre de type A
qui nécessite un paramètre implicite de type M[A]
:
def foo[A](implicit ma: M[A])
peut être réécrit comme:
def foo[A: M]
mais à quoi bon passer le paramètre implicite sans le nommer? Comment cela peut-il être utile lors de la mise en œuvre de la méthode foo
?
Souvent, le paramètre implicite n'a pas besoin d'être renvoyé directement, il sera passé à travers implicite d'argument à une autre méthode qui est appelée. Si cela est nécessaire, vous pouvez toujours conserver la signature de la méthode terse avec le contexte lié, et appeler implicitly
pour matérialiser la valeur:
def foo[A: M] = {
val ma = implicitly[M[A]]
}
passant explicitement un sous-ensemble de paramètres implicites
supposons que vous appelez une méthode qui imprime une personne, en utilisant une approche basée sur la classe de type:
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)
et si nous voulez changer la façon dont le nom est sortie? Nous pouvons explicitement appeler PersonShow
, passer explicitement une alternative Show[String]
, mais nous voulons que le compilateur passe le Show[Int]
.
Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)
Implicitly
est disponible dans Scala 2.8 et est défini dans Predef comme:
def implicitly[T](implicit e: T): T = e
il est communément utilisé pour vérifier si une valeur implicite de type est disponible et le retourner si tel est le cas.
exemple Simple de présentation de 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]
^
Un "vous apprendre à pêcher", la réponse est d'utiliser le classement alphabétique index de membre actuellement disponibles dans le Scaladoc nightlies . Les lettres (et le #
, pour les noms non-alphabétiques) en haut du volet paquet / Classe sont des liens vers l'index pour les noms des membres commençant par cette lettre (à travers toutes les classes). Si vous choisissez I
, par exemple, vous trouverez l'entrée implicitly
avec une occurrence, dans Predef
, que vous pouvez visiter à partir du lien.