J'ai un cas où je souhaite appliquer des modifications à un objet basé sur la présence de (quelques, disons, 5 à 10) options. Donc, en gros, si je devais le faire impérieusement, ce que je vise est:De gauche à droite arguments type inférence
var myObject = ...
if (option.isDefined) {
myObject = myObject.modify(option.get)
}
if (option2.isDefined) {
myObject = myObject.someOtherModification(option2.get)
}
(S'il vous plaît noter: peut-être mon objet est mutable, peut-être pas, ce n'est pas le point ici.)
Je pensais que ce serait plus joli si je tentais de mettre en œuvre un moyen couramment la rédaction du présent, par exemple (code de pseudo ...):
myObject.optionally(option, _.modify(_))
.optionally(option2, _.someOtherModification(_))
Alors j'ai commencé avec un exemple de code qui ne IntelliJ point fort comme une erreur, mais cela ne construit pas réellement.
class MyObject(content: String) {
/** Apply a transformation if the optional is present */
def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject =
optional.map(operation(_, this)).getOrElse(this)
/** Some possible transformation */
def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
object Test {
val my = new MyObject("test")
val option = Option(2)
my.optionally(option, (size, value) => value.resized(size))
}
Maintenant, dans mon cas, le type MyObject
est d'une API externe, donc je créé une conversion implicite pour aider, donc ce qu'il ressemble vraiment à:
// Out of my control
class MyObject(content: String) {
def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
// What I did : create a rich type over MyObject
class MyRichObject(myObject: MyObject) {
def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject = optional.map(operation(_, myObject)).getOrElse(myObject)
}
// And an implicit conversion
object MyRichObject {
implicit def apply(myObject: MyObject): MyRichObject = new MyRichObject(myObject)
}
Et puis, je l'utilise de cette façon:
object Test {
val my = new MyObject("test")
val option = Option(2)
import MyRichObject._
my.optionally(option, (size, value) => value.resized(size))
}
et cette fois, il échoue dans IntelliJ et lors de la compilation parce que le type de Option
est inconnu: Error:(8, 26) missing parameter type my.optionally(option, (size, value) => value.resized(size))
Pour le faire fonctionner, je peux:
- spécifier activement un type de l'argument
size
:my.optionally(option, (size: Int, value) => value.resized(size))
- Réécrire le
optionally
à une version cari
Aucun d'entre eux est vraiment mauvais, mais si je peux demander:
- Y at-il une raison pour laquelle une version cari fonctionne, mais une version d'arguments multiples semble ne pas déduire le type paramétrisé,
- Pourrait-il être écrit d'une manière qui fonctionne sans spécifier les types réels
- et comme bonus (bien que cela puisse être basé sur l'opinion), comment l'écririez-vous (une sorte de
foldLeft
sur une séquence d'options me vient à l'esprit ...)?
J'aime l'idée d'inverser les implicits au type Optional, au lieu de MyObject (généralisation: rendre le type paramétrable convertible, éliminant ainsi toute méthode paramétrée à l'autre extrémité). Je suis d'accord avec votre conclusion: la version au curry semble au moins aussi bonne. M'a fait penser qu'il pourrait y avoir quelque chose à essayer avec les types de témoins (https://stackoverflow.com/questions/8524878/implicit-conversion-vs-type-class?rq=1) – GPI
Voir aussi http: //pchiusano.blogspot. hk/2011/05/making-most-of-scalas-extrêmement-limited.html à propos de la limitation sur l'inférence de type que vous avez observée – PH88
Merci d'avoir creusé cela. Ce que je ne comprends pas, c'est que dans le premier exemple d'implémentation (sans implicits), cela fonctionne réellement ... – GPI