Dans Scala, l'interaction de la surcharge et de la résolution implicite des arguments semble rendre impossible l'utilisation du code suivant.Comment contourner cette limitation de la résolution de surcharge dans Scala?
trait Bijection[A, B] extends Function1[A, B] with Unapply[A, B] { self =>
def apply(a: A): B
def unapply(b: B): A
}
sealed trait Unapply[A, B] {
def unapply(b: B): A
}
object Bijection {
implicit def biject[A](a: A): Biject[A] = new Biject(a)
implicit object IntStringBijection extends Bijection[Int, String] {
override def apply(a: Int): String = a.toString
override def unapply(b: String): Int = b.toInt
}
}
sealed class Biject[A](a: A) {
def as[B](implicit f: Function1[A, B]): B = f(a)
def as[B](implicit f: Unapply[B, A]): B = f unapply a
}
L'objectif est ici pour a.as [B] pour effectuer une conversion typesafe indépendamment du fait que une bijection [A, B] ou une bijection [B, A] est disponible portée implicite. La raison pour laquelle cela ne fonctionne pas est que la résolution implicite semble avoir lieu après une désambiguïsation de surcharge dans le compilateur, et puisque les deux implémentations de 'as' ont le même type de résultat, le compilateur ne parvient même pas à tenter de découvrir si un implicite approprié est dans la portée qui peut effectuer la conversion. En résumé, la résolution implicite n'est pas utilisée dans la désambiguïsation de surcharge.
La raison pour laquelle je veux que 'as' soit surchargé est d'éviter à l'utilisateur de cette bibliothèque de devoir coder la "direction" de la bijection sur le site d'appel; évidemment on pourrait mettre en œuvre Biject comme ceci:
sealed class Biject[A](a: A) {
def viaForward[B](implicit f: Function1[A, B]): B = f(a)
def viaReverse[B](implicit f: Unapply[B, A]): B = f unapply a
}
mais ce qui est vraiment peu attrayante car elle rend essentiellement le proxénète superflu; on pourrait aussi bien passer explicitement la bijection, mais bien sûr vous perdez la possibilité de faire varier la bijection en fonction de la portée.
Y at-il une bonne solution à ce problème?
C'est génial, retronym, merci! Il m'est venu à l'esprit que l'un et l'autre pourraient avoir besoin de jouer un rôle, mais il ne m'est jamais venu à l'esprit de projeter la bijection elle-même dans l'un ou l'autre espace. Si je comprends bien, la raison pour laquelle cela fonctionne est la raison même de ma tentative initiale: la conversion implicite initiale en Biject est "complète" avant la résolution implicite pour le paramètre "as", ce qui permet la gauche et la droite conversions implicites à appliquer. –
La clé pour cela est d'utiliser la priorisation implicite pour éviter l'ambiguïté dans le cas de '1.as [Int]'. – retronym
Il semble que cela fonctionne même sans le piège implicits de faible priorité. Les deux left() et right() peuvent être déclarés dans l'objet Bijection, (et ils peuvent être spécialisés afin qu'ils ne s'appliquent qu'aux instances de Bijection) et cela fonctionne toujours. –