Comprendre implicite dans Scala

Je parcourais le didacticiel Scala playframework et je suis tombé sur cet extrait de code qui me laissait perplexe:

def newTask = Action { implicit request => taskForm.bindFromRequest.fold( errors => BadRequest(views.html.index(Task.all(), errors)), label => { Task.create(label) Redirect(routes.Application.tasks()) } ) } 

J’ai donc décidé d’enquêter et je suis tombé sur ce post .

Je ne comprends toujours pas.

Quelle est la différence entre ceci:

 implicit def double2Int(d : Double) : Int = d.toInt 

et

 def double2IntNonImplicit(d : Double) : Int = d.toInt 

autre que le fait évident qu’ils ont des noms de méthodes différents.

Quand devrais-je utiliser implicit et pourquoi?

Je vais vous expliquer les principaux cas d’utilisation d’implicits ci-dessous, mais pour plus de détails, voir le chapitre pertinent de Programmation dans Scala .

Paramètres implicites

La liste de parameters finale sur une méthode peut être marquée implicit , ce qui signifie que les valeurs seront extraites du contexte dans lequel elles sont appelées. S’il n’y a pas de valeur implicite du type approprié dans la scope, celle-ci ne sera pas compilée. Puisque la valeur implicite doit se résoudre en une seule valeur et pour éviter les collisions, il est recommandé de définir le type en fonction de son objective, par exemple, ne pas avoir besoin de vos méthodes pour trouver une Int implicite!

Exemple:

  // probably in a library class Prefixer(val prefix: Ssortingng) def addPrefix(s: Ssortingng)(implicit p: Prefixer) = p.prefix + s // then probably in your application implicit val myImplicitPrefixer = new Prefixer("***") addPrefix("abc") // returns "***abc" 

Conversions implicites

Lorsque le compilateur trouve une expression du type incorrect pour le contexte, il cherchera une valeur de Function implicite d’un type qui lui permettra de détecter les caractères. Donc, si un A est requirejs et qu’il trouve un B , il recherchera une valeur implicite de type B => A dans la scope (il vérifie également d’autres endroits, comme dans les objects compagnons B et A , s’ils existent). Comme def s peut être “étendu” dans Function objects Function , un implicit def xyz(arg: B): A fera aussi.

Donc, la différence entre vos méthodes est que celui marqué implicit sera inséré pour vous par le compilateur quand un Double est trouvé mais un Int est requirejs.

 implicit def doubleToInt(d: Double) = d.toInt val x: Int = 42.0 

travaillera le même que

 def doubleToInt(d: Double) = d.toInt val x: Int = doubleToInt(42.0) 

Dans la seconde, nous avons inséré la conversion manuellement; dans le premier, le compilateur a fait la même chose automatiquement. La conversion est requirejse en raison de l’annotation de type sur le côté gauche.


Concernant votre premier extrait de Play:

Les actions sont expliquées sur cette page à partir de la documentation Play (voir aussi les documents API ). Vous utilisez

 apply(block: (Request[AnyContent]) ⇒ Result): Action[AnyContent] 

sur l’object Action (qui est le compagnon du trait du même nom).

Nous devons donc fournir une fonction comme argument, qui peut être écrit sous forme littérale dans le formulaire.

 request => ... 

Dans un littéral de fonction, la partie avant le => est une déclaration de valeur et peut être marquée implicit si vous le souhaitez, comme dans toute autre déclaration de val . Ici, la request n’a pas besoin d’être marquée implicit pour qu’elle puisse taper check, mais ce faisant, elle sera disponible en tant que valeur implicite pour toutes les méthodes qui pourraient en avoir besoin dans la fonction (et bien sûr, elle peut être utilisée explicitement comme bien). Dans ce cas particulier, cela a été effectué car la méthode bindFromRequest de la classe Form nécessite un argument Request implicite.

ATTENTION: contient du sarcasme judicieusement! YMMV …

La réponse de Luigi est complète et correcte. Celui-ci vise seulement à vous donner un exemple de la façon dont vous pouvez abuser des implicites , comme cela se produit souvent dans les projets Scala. En fait, si souvent, vous pouvez probablement le trouver dans l’un des guides “Best Practice” .

 object HelloWorld { case class Text(content: Ssortingng) case class Prefix(text: Ssortingng) implicit def Ssortingng2Text(content: Ssortingng)(implicit prefix: Prefix) = { Text(prefix.text + " " + content) } def printText(text: Text): Unit = { println(text.content) } def main(args: Array[Ssortingng]): Unit = { printText("World!") } // Best to hide this line somewhere below a stack of completely unrelated code. // Better yet, import its package from another distant place. implicit val prefixLOL = Prefix("Hello") } 

Pourquoi et quand vous devez marquer le paramètre de request comme implicit :

Certaines méthodes que vous utiliserez dans le corps de votre action ont une liste de parameters implicite comme, par exemple, Form.scala définit une méthode:

 def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = { ... } 

Vous ne le remarquez pas forcément comme si vous myForm.bindFromRequest() simplement myForm.bindFromRequest() Vous n’avez pas à fournir explicitement les arguments implicites. Non, vous laissez le compilateur rechercher tout object candidat valide à transmettre chaque fois qu’il rencontre un appel de méthode nécessitant une instance de la demande. Comme vous avez une demande disponible, il vous suffit de la marquer comme implicit .

Vous le marquez explicitement comme disponible pour une utilisation implicite .

Vous faites allusion au compilateur qu’il est “OK” d’utiliser l’object de requête envoyé par le framework Play (que nous avons donné le nom “request” mais qu’il aurait pu utiliser “r” ou “req”) à tout moment, “en cachette” .

 myForm.bindFromRequest() 

le voir? ce n’est pas là, mais c’est là!

Cela se produit simplement sans que vous ayez à le placer manuellement dans tous les endroits où vous en avez besoin (mais vous pouvez le faire explicitement, si vous le souhaitez, peu importe s’il est marqué implicit ou non):

 myForm.bindFromRequest()(request) 

Sans le marquer comme implicite, vous devriez faire ce qui précède. Le marquer comme implicite, vous n’avez pas à le faire.

Quand devriez-vous marquer la demande comme implicit ? Vous ne devez vraiment le faire que si vous utilisez des méthodes qui déclarent une liste de parameters implicite dans laquelle une instance de la requête est attendue . Mais pour restr simple, vous pouvez prendre l’habitude de toujours marquer la demande implicit . De cette façon, vous pouvez simplement écrire de beaux codes laconiques.

De plus, dans le cas ci-dessus, il ne devrait y avoir only one fonction implicite dont le type est double => Int . Sinon, le compilateur devient confus et ne comstack pas correctement.

 //this won't comstack implicit def doubleToInt(d: Double) = d.toInt implicit def doubleToIntSecond(d: Double) = d.toInt val x: Int = 42.0