Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ambigious implicits

The question is why doesn't the following code work with type inference (below is a REPL session to demonstrate), and can it be fixed? More specifically, how is this different from the use of CanBuildFrom which the compiler uses to infer the return type?

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

I have this session in the 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 
like image 473
IttayD Avatar asked Mar 22 '11 12:03

IttayD


4 Answers

I can see why you'd expect it to work, but, obviously, the type inferencer isn't using the return type to infer T. I'd expect it too as well.

As for the ambiguity, CanBuildFrom avoids being ambiguous by just not defining everything in the same "level". For instance, this solves the ambiguity problem:

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

However, it will not make the type inference work the way you want:

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

Which indicates it obviously makes T inference without taking into consideration the return type.

like image 138
Daniel C. Sobral Avatar answered Sep 28 '22 15:09

Daniel C. Sobral


> trait UrlLike[T] {

trait UrlLike[+T] {
like image 31
psp Avatar answered Sep 28 '22 14:09

psp


Regarding any implicit ambiguity, the rule is (since Scala2.8):

When comparing two different applicable alternatives of an overloaded method or of an implicit, each method:

  • gets one point for having more specific arguments,
  • and another point for being defined in a proper subclass.

An alternative “wins” over another if it gets a greater number of points in these two comparisons.
This means in particular that if alternatives have identical argument types, the one which is defined in a subclass wins.

I don't think the implicits around URL or URI get a different set of points following those criteria.

like image 34
VonC Avatar answered Sep 28 '22 14:09

VonC


It might not be obvious from the error report that you're seeing, but all three of the implicits defined in object UrlLike are contributing to the ambiguity (eg. try commenting out the definition of uri and you'll see the ambiguity reported as being between str and url).

The reason for the ambiguity is that the type parameter T of UrlSupport.url is bounded only by the requirement that there be an implicit UrlLike instance available for it. String, URL and URI all satisfy that requirement equally well thanks the UrlLike instances provided by your three implicit objects. The compiler isn't going to choose one of those for you arbitrarily, so it reports an ambiguity.

Unless, of course, you resolve the ambiguity by explicitly specifying the type argument, as you've done in the last two of your REPL interactions.

like image 25
Miles Sabin Avatar answered Sep 28 '22 15:09

Miles Sabin