Je faisais mon chemin dans le tutoriel playframework Scala et je suis tombé sur cet extrait de code qui m'avait laissé 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 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, ils ont des noms de méthode différents.
Quand devrais-je utiliser implicit
et pourquoi?
J'expliquerai les principaux cas d'utilisation des implicites ci-dessous, mais pour plus de détails, voir le chapitre correspondant de la Programmation en Scala .
Paramètres implicites
La liste finale des paramètres d'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 bon type dans la portée, la compilation n'est pas effectuée. Étant donné que la valeur implicite doit être résolue en une valeur unique et éviter les conflits, il est judicieux de définir le type en fonction de son objectif, par exemple. N’avez pas besoin de vos méthodes pour trouver un Int
implicite!
exemple:
// probably in a library
class Prefixer(val prefix: String)
def addPrefix(s: String)(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 recherche une valeur implicite Function
d'un type lui permettant de procéder à une vérification de type. Donc, si un A
est requis et qu'il trouve un B
, il recherchera une valeur implicite de type B => A
dans la portée (il vérifie également d'autres endroits comme dans le B
et A
objets associés, s'ils existent). Puisque def
s peut être "eta-étendu" en Function
objets, une implicit def xyz(arg: B): A
fera également l'affaire.
Ainsi, la différence entre vos méthodes est que celle marquée implicit
sera insérée par le compilateur quand un Double
sera trouvé mais qu'un Int
sera requis.
implicit def doubleToInt(d: Double) = d.toInt
val x: Int = 42.0
fonctionnera comme
def doubleToInt(d: Double) = d.toInt
val x: Int = doubleToInt(42.0)
Dans le second cas, nous avons inséré la conversion manuellement. dans le premier le compilateur a fait la même chose automatiquement. La conversion est nécessaire en raison de l'annotation de type sur le côté gauche.
En ce qui concerne votre premier extrait de Play:
Les actions sont expliquées dans cette page dans la documentation de Play (voir aussi documents d'API ). Vous utilisez
apply(block: (Request[AnyContent]) ⇒ Result): Action[AnyContent]
sur l'objet Action
(qui accompagne le trait du même nom).
Nous avons donc besoin de fournir une fonction comme argument, qui peut être écrit sous forme littérale sous la forme
request => ...
Dans un littéral de fonction, la partie précédant le =>
est une déclaration de valeur et peut être marquée implicit
si vous le souhaitez, comme dans toute autre déclaration val
. Ici, request
ne doit pas être marqué implicit
pour cela, mais ce 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 également être utilisée explicitement). Dans ce cas particulier, cela a été fait car la méthode bindFromRequest
de la classe Form requiert un argument implicite Request
.
ATTENTION: contient judicieusement le sarcasme! YMMV ...
La réponse de Luigi est complète et correcte. Celui-ci est seulement pour l'étendre un peu avec un exemple de la façon dont vous pouvez abuser glorieusement implique, comme cela arrive souvent dans les projets Scala. En fait si souvent, vous pouvez probablement même le trouver dans l'un des "Meilleures pratiques".
object HelloWorld {
case class Text(content: String)
case class Prefix(text: String)
implicit def String2Text(content: String)(implicit prefix: Prefix) = {
Text(prefix.text + " " + content)
}
def printText(text: Text): Unit = {
println(text.content)
}
def main(args: Array[String]): Unit = {
printText("World!")
}
// Best to hide this line somewhere below a pile 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 request
comme implicit
:
Certaines méthodes que vous utiliserez dans le corps de votre action ont une liste de paramètres implicites comme, par exemple, Form.scala définit une méthode:
def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = { ... }
Vous ne le remarquerez pas nécessairement comme vous appelleriez simplement myForm.bindFromRequest()
Vous n'avez pas à fournir explicitement les arguments implicites. Non, vous laissez le compilateur pour rechercher tout objet candidat valide à transmettre chaque fois qu'il rencontre un appel de méthode nécessitant une instance de la demande. Puisque vous do avez une requête disponible, tout ce que vous avez à faire est de la marquer comme implicit
.
Vous explicitement marquez-le comme étant disponible pour implicite utilisez.
Vous indiquez au compilateur qu'il est "correct" d'utiliser l'objet de requête envoyé par le framework Play (que nous avons appelé "requête" mais que nous aurions pu simplement utiliser "r" ou "req") si nécessaire, "en cachette" .
myForm.bindFromRequest()
le voir? ce n'est pas là, mais c'est est là!
Cela se produit sans que vous ayez à l'insérer manuellement à chaque endroit où vous en avez besoin (mais vous pouvez le passez explicitement, si vous le souhaitez, même s'il est marqué implicit
ou pas):
myForm.bindFromRequest()(request)
Sans le marquer comme implicite, vous devez faire ce qui précède. En le marquant comme implicite, vous n'êtes pas obligé.
Quand devez-vous marquer la demande comme implicit
? Vous n’avez vraiment besoin de le faire que si vous utilisez des méthodes qui déclarent une liste de paramètres implicites attend une instance de Request . Mais pour simplifier les choses, vous pouvez simplement prendre l’habitude de marquer la requête implicit
toujours . De cette façon, vous pouvez simplement écrire de beaux codes laconiques.
De plus, dans le cas ci-dessus, il devrait y avoir only one
fonction implicite de type double => Int
. Sinon, le compilateur devient confus et ne compilera pas correctement.
//this won't compile
implicit def doubleToInt(d: Double) = d.toInt
implicit def doubleToIntSecond(d: Double) = d.toInt
val x: Int = 42.0
Dans scala implicite fonctionne comme:
Convertisseur
Injecteur de valeur de paramètre
Il existe 3 types d'utilisation d'Implicit
Conversion de type implicite}: convertit l'affectation génératrice d'erreur en type prévu
val x: String = "1"
val y: Int = x
String n'est pas le sous-type de Int, donc une erreur survient à la ligne 2. Pour résoudre l'erreur, le compilateur recherche une telle méthode dans l'étendue qui a un mot clé implicite et prend une String comme argument et retourne un Int.
alors
implicit def z(a:String):Int = 2
val x :String = "1"
val y:Int = x // compiler will use z here like val y:Int=z(x)
println(y) // result 2 & no error!
Conversion implicite du récepteur: Nous avons généralement, par exemple, les propriétés de l'objet d'appel du destinataire. méthodes ou variables. Donc, pour appeler une propriété par un destinataire, la propriété doit être le membre de la classe/de l'objet de ce destinataire.
class Mahadi{
val haveCar:String ="BMW"
}
class Johnny{
val haveTv:String = "Sony"
}
val mahadi = new Mahadi
mahadi.haveTv // Error happening
Ici, (mahadi.haveTv} générera une erreur. Parce que scala le compilateur va d'abord rechercher la propriété haveTv dans mahadi receiver. Il ne trouvera pas. Deuxièmement, il recherchera une méthode de portée ayant mot-clé implicite qui prend objet Mahadi comme argument et retourne objet Johnny. Mais ça n'a pas ici. Donc, cela va créer erreur. Mais ce qui suit est d'accord.
class Mahadi{
val haveCar:String ="BMW"
}
class Johnny{
val haveTv:String = "Sony"
}
val mahadi = new Mahadi
implicit def z(a:Mahadi):Johnny = new Johnny
mahadi.haveTv // compiler will use z here like new Johnny().haveTv
println(mahadi.haveTv)// result Sony & no error
Injection implicite de paramètre: si nous appelons une méthode et ne transmettons pas sa valeur de paramètre, cela provoquera une erreur. Le compilateur scala fonctionne comme ceci - le premier essayera de transmettre la valeur, mais il n’aura aucune valeur directe pour le paramètre.
def x(a:Int)= a
x // ERROR happening
Deuxièmement, si le paramètre a un mot-clé implicite, il cherchera tous les val dans la portée qui ont le même type de valeur. Sinon, cela provoquera une erreur.
def x(implicit a:Int)= a
x // error happening here
Pour résoudre ce problème, le compilateur cherchera un valeur implicite ayant le type de Int parce que le paramètre a a mot clé implicite.
def x(implicit a:Int)=a
implicit val z:Int =10
x // compiler will use implicit like this x(z)
println(x) // will result 10 & no error.
n autre exemple:
def l(implicit b:Int)
def x(implicit a:Int)= l(a)
on peut aussi l'écrire comme-
def x(implicit a:Int)= l
Parce que l a un paramètre implicite et dans la portée de corps de la méthode x, il y a un variable locale implicite _ ( les paramètres sont variables locales ) a qui est le paramètre de x, donc dans le corps de xméthode la signature de méthode l'argument implicite de la valeur est classé par la variable implicite locale de la méthode x (paramètre) a
implicitement}.
Alors
def x(implicit a:Int)= l
sera en compilateur comme ça
def x(implicit a:Int)= l(a)
n autre exemple:
def c(implicit k:Int):String = k.toString
def x(a:Int => String):String =a
x{
x => c
}
cela provoquera une erreur, car c dans x {x => c} a besoin de passer explicitement en valeur ou en valeur implicite dans la portée.
Ainsi, nous pouvons rendre paramètre explicitement implicite de la fonction littéral lorsque nous appelons le méthode x
x{
implicit x => c // the compiler will set the parameter of c like this c(x)
}
Ceci a été utilisé dans méthode d'action de Play-Framework
def index = Action{
implicit request =>
Ok(views.html.formpage)
}