Question
La question est de savoir pourquoi le code suivant ne fonctionne pas avec l'inférence de type (ci-dessous est une session REPL à démontrer), et peut-il être corrigé?Plus précisément, en quoi est-ce différent de l'utilisation de CanBuildF à partir de laquelle le compilateur utilise pour déduire le type de retour?
Compte tenu de ce code:
object S {
import java.net._
trait UrlLike[T] {
def url(s: String): T
}
object UrlLike {
implicit object str extends UrlLike[String]{def url(s: String) = s}
implicit object url extends UrlLike[URL]{def url(s: String) = new URL(s)}
implicit object uri extends UrlLike[URI]{def url(s: String) = new URI(s)}
}
trait UrlSupport {
val _url: String
def url[T : UrlLike]: T = implicitly[UrlLike[T]].url(_url)
}
}
J'ai cette session dans le REPL (2.8.1):
scala> :load c:\temp\UrlTest.scala
Loading c:\temp\UrlTest.scala...
defined module S
scala> import java.net._
import java.net._
scala> import S._
import S._
scala> new UrlSupport{val _url = "http://example.com"}
res0: java.lang.Object with S.UrlSupport = $anon$1@155bd22
scala> res0.url : String
<console>:14: error: ambiguous implicit values:
both object uri in object UrlLike of type object S.UrlLike.uri
and object url in object UrlLike of type object S.UrlLike.url
match expected type S.UrlLike[T]
res0.url : String
^
scala> res0.url : URL
<console>:14: error: ambiguous implicit values:
both object uri in object UrlLike of type object S.UrlLike.uri
and object url in object UrlLike of type object S.UrlLike.url
match expected type S.UrlLike[T]
res0.url : URL
^
scala> res0.url[String]
res3: String = http://example.com
scala> res0.url[URL]
res4: java.net.URL = http://example.com
La solution
> trait UrlLike[T] {
trait UrlLike[+T] {
Autres conseils
Je peux voir pourquoi vous vous attendez à ce que cela fonctionne, mais, évidemment, l'inférence de type n'utilise pas le type de retour pour inférer T
.Je m'y attendrais aussi.
Quant à l'ambiguïté, CanBuildFrom
évite d'être ambigu en ne définissant simplement pas tout dans le même "niveau".Par exemple, cela résout le problème d'ambiguïté:
trait LowPriorityImplicits {
implicit object url extends UrlLike[URL]{def url(s: String) = new URL(s)}
implicit object uri extends UrlLike[URI]{def url(s: String) = new URI(s)}
}
object UrlLike extends LowPriorityImplicits {
implicit object str extends UrlLike[String]{def url(s: String) = s}
}
Cependant, cela ne fera pas que l'inférence de type fonctionne comme vous le souhaitez:
scala> res0.url : URL
<console>:16: error: type mismatch;
found : String
required: java.net.URL
res0.url : URL
^
Ce qui indique qu'il fait évidemment une inférence T
sans prendre en compte le type de retour.
En ce qui concerne toute ambiguïté implicite, la règle est (depuis Scala2.8) :
Lors de la comparaison de deux alternatives applicables différentes d'une méthode surchargée ou d'une implicite, chaque méthode:
- obtient un point pour avoir des arguments plus spéci fi ques ,
- et un autre point pour être défini dans une sous-classe appropriée .
Une alternative "l'emporte" sur une autre si elle obtient un plus grand nombre de points dans ces deux comparaisons.
Cela signifie en particulier que si les alternatives ont des types d'arguments identiques, celui qui est défini dans une sous-classe l'emporte.
Je ne pense pas que les implicites autour de URL
ou URI
obtiennent un ensemble de points différent suivant ces critères.
Cela n'est peut-être pas évident d'après le rapport d'erreur que vous voyez, mais les trois implicits définis dans l'objet UrlLike contribuent à l'ambiguïté (par exemple, essayez de commenter la définition de uri et vous verrez l'ambiguïtésignalé comme étant entre str et url).
La raison de l'ambiguïté est que le paramètre de type T de UrlSupport.url n'est limité que par l'exigence qu'une instance UrlLike implicite soit disponible pour lui.La chaîne, l'URL et l'URI satisfont également à cette exigence grâce aux instances UrlLike fournies par vos trois objets implicites.Le compilateur ne choisira pas arbitrairement l'un de ceux-ci pour vous, il signale donc une ambiguïté.
À moins, bien sûr, que vous ne résolviez l'ambiguïté en spécifiant explicitement l'argument de type, comme vous l'avez fait dans les deux dernières de vos interactions REPL.