Voici quelques raisons d'utiliser la méthode délicieusement simple implicitly
.
Pour comprendre / dépanner 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 de membre selection
applicable à args
(même après avoir tenté de convertir args
avec des vues implicites). Dans ce cas, le compilateur recherche des membres implicites, définis localement dans les étendues actuelles ou englobantes, héritées ou importées, qui sont soit des fonctions du type de celui-ci the.prefix
vers un type avec des selection
méthodes implicites définies, soit é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 également ê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 recherche 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 fonctionnalité plus importante de Scala que les vues implicites. Ils prennent en charge le modèle de classe de type. La bibliothèque standard l'utilise à quelques endroits - voyez scala.Ordering
et comment elle est utilisée dans SeqLike#sorted
. Les paramètres implicites sont également utilisés pour transmettre des manifestes Array et des CanBuildFrom
instances.
Scala 2.8 permet une syntaxe abrégée pour les paramètres implicites, appelée Contexte Bounds. En bref, 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 ça sert de 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 référencé directement, il sera acheminé en tant qu'argument implicite vers une autre méthode appelée. Si nécessaire, vous pouvez toujours conserver la signature de méthode laconique avec le Contexte Bound et appeler implicitly
pour matérialiser la valeur:
def foo[A: M] = {
val ma = implicitly[M[A]]
}
Passer un sous-ensemble de paramètres implicites explicitement
Supposons que vous appeliez une méthode qui imprime assez une personne, en utilisant une approche basée sur une 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 voulons changer la façon dont le nom est affiché? Nous pouvons appeler PersonShow
explicitement, 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)