Qu'est-ce que l'identifiant Scala «implicitement»?

169

J'ai vu une fonction nommée implicitlyutilisée dans des exemples Scala. Qu'est-ce que c'est et comment est-il utilisé?

Exemple ici :

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

Notez que nous devons écrire implicitly[Foo[A]].apply(x)puisque le compilateur pense que cela implicitly[Foo[A]](x)signifie que nous appelons implicitlyavec des paramètres.

Voir également Comment rechercher des objets / types / etc. de Scala REPL? et Où Scala cherche-t-il les implicites?

oluies
la source

Réponses:

206

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 selectionapplicable à args(même après avoir tenté de convertir argsavec 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.prefixvers un type avec des selectionmé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.Orderinget comment elle est utilisée dans SeqLike#sorted. Les paramètres implicites sont également utilisés pour transmettre des manifestes Array et des CanBuildFrominstances.

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 Aqui 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 implicitlypour 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 PersonShowexplicitement, 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)
rétronyme
la source
2
scala> 1.min (2) res0: Int = 1 Dans Scala 2.10.3 j'obtiens une erreur: scala> implicitement [Int => {def min (i: Int): Any}] <console>: 8: erreur: Aucune vue implicite disponible depuis Int => AnyRef {def min (i: Int): Any}. implicitement [Int => {def min (i: Int): Any}]
jhegedus
Cette réponse serait mise à jour pour la dernière version.
emeth
1
implicitement [Int => AnyVal {def min (i: Int): Int}] fonctionnera. Doit être corrigé dans la réponse.
Malkaviano
212

Implicitlyest disponible dans Scala 2.8 et est défini dans Predef comme:

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

Il est couramment utilisé pour vérifier si une valeur implicite de type Test disponible et la renvoyer si tel est le cas.

Exemple simple de la 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]
                         ^
oluies
la source
6
La méthode ne vérifie pas exactement; il semble provoquer une erreur de compilation s'il n'y a pas de valeur implicite disponible et, s'il y en a, semble la récupérer. Pouvez-vous nous expliquer pourquoi je voudrais utiliser cela?
davetron5000
17
implicitly[Ordering[(Int, String)]].compare( (1, "b"), (1, "a") ), notamment pour récupérer un paramètre implicite introduit par un Contexte Bound:def foo[A: Ordering](a1: A, a2: A) = implicitly[Ordering[A]].compare(a1, a2)
retronym
1
Pour voir la discussion de retronym dans le lien vidéo ci-dessus, passez au point 13:50.
chaotic3quilibrium
-2

Une réponse «apprenez-vous à pêcher» est d'utiliser l'index alphabétique des membres actuellement disponible dans les nightlies Scaladoc . Les lettres (et le #, pour les noms non alphabétiques) en haut du volet package / classe sont des liens vers l'index des noms de membres commençant par cette lettre (dans toutes les classes). Si vous choisissez I, par exemple, vous trouverez l' implicitlyentrée avec une occurrence, dans Predef, que vous pouvez visiter à partir du lien.

Randall Schulz
la source
46
Bien sûr, ces scaladocs ne disent rien du tout implicitement, donc cela ne compte guère comme documentation. Comment quelqu'un pourrait-il comprendre ce que cette méthode fait à partir de ces seuls documents? Je me sens régulièrement déçu par la documentation Scala. Le comportement des méthodes comme implicitement est loin d'être évident, et la documentation à leur sujet est à peine meilleure qu'inexistante. Merci mon Dieu pour Stack Overflow. / end rant
Jeff
4
La signature de type documente assez bien celle-ci.
retronym
21
implicitsemble être une caractéristique importante du langage dans Scala, et certainement digne d'une explication appropriée. Penser que les documents ne détaillant qu'un nombre de signatures de type ressemble plus à une auto-satisfaction intellectuelle qu'à une véritable réponse. Voir les questions spécifiques posées par le PO - qu'est-ce que c'est et comment est-il utilisé? Ni répondu par cela, ni dans les documents nocturnes auxquels vous ne fournissez même pas de lien réel. scala-lang.org/files/archive/nightly/docs/library/… Cela n'apprend rien. Voir Niklaus Wirth ou Turbo Pascal pour des exemples de documents authentiques. -1
Thomas W
3
implicitet implicitlysont liés, mais assez distincts. Le implicitmot-clé fait partie de la langue. implicitlyest défini en code Scala simple dans la bibliothèque standard. Étant donné que les documents en ligne incluent des liens sources, je pense qu'il est toujours préférable de renvoyer les personnes interrogées vers ces documents et la source liée.
Randall Schulz