2011-03-22 18 views
8

La pregunta es por qué el siguiente código no funciona con inferencia de tipo (a continuación se muestra una sesión de REPL para demostrar), y ¿se puede arreglar? Más específicamente, ¿cómo se diferencia esto del uso de CanBuildFrom que el compilador usa para inferir el tipo de devolución?implicits ambiguos

Teniendo en cuenta este código:

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) 
    } 
} 

tengo esta sesión en el 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 = [email protected] 

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 

Respuesta

4
> trait UrlLike[T] { 

trait UrlLike[+T] { 
2

relación con cualquier ambigüedad implícita, la rule is (since Scala2.8):

Cuando comparar dos alternativas aplicables diferentes de un método sobrecargado o de un método implícito, cada uno:

  • obtiene un punto por que tienen argumentos fi cos más,
  • y otro punto para se define en una subclase apropiada.

Una alternativa "gana" sobre otra si obtiene un mayor número de puntos en estas dos comparaciones.
Esto significa en particular que si las alternativas tienen tipos de argumentos idénticos, el que se define en una subclase gana.

No creo que los implícitos alrededor URL o URI obtener un conjunto diferente de puntos Basándose en estos criterios.

2

Puede que no sea obvio por el informe de error que está viendo, pero las tres implícitas definidas en el objeto UrlLike están contribuyendo a la ambigüedad (por ej. Intente comentando la definición de uri y verá el ambigüedad reportada como entre str y url).

El motivo de la ambigüedad es que el parámetro de tipo T de UrlSupport.url está limitado solo por el requisito de que haya una instancia implícita de UrlLike disponible para él. String, URL y URI satisfacen todos los requisitos igualmente bien gracias a las instancias de UrlLike proporcionadas por sus tres objetos implícitos. El compilador no va a elegir uno de ellos arbitrariamente, por lo que informa una ambigüedad.

A menos que, por supuesto, resuelva la ambigüedad especificando explícitamente el argumento de tipo, como lo ha hecho en las dos últimas interacciones de REPL.

+0

¿No ve el compilador que quiero un resultado de String/URL/URI debido al tipo de adscripción? – IttayD

5

Puedo ver por qué esperas que funcione, pero, obviamente, el tipo inferencer no usa el tipo de devolución para inferir T. Yo también lo esperaría.

En cuanto a la ambigüedad, CanBuildFrom evita ser ambiguo al no definir todo en el mismo "nivel".Por ejemplo, esto resuelve el problema de la ambigüedad:

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} 
} 

Sin embargo, será no hacer que la inferencia de tipos funcione de la manera deseada:

scala> res0.url : URL 
<console>:16: error: type mismatch; 
found : String 
required: java.net.URL 
     res0.url : URL 
      ^

Lo que indica que, evidentemente, hace T inferencia sin tener en consideración del tipo de devolución.

Cuestiones relacionadas