2011-03-22 72 views
8

问题是为什么下面的代码不能使用类型推断(下面是要演示的REPL会话),它可以被修复吗?更具体地说,这和编译器用来推断返回类型的CanBuildFrom的用法有什么不同?含糊不清的暗示

鉴于此代码:

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

我在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 

回答

4
> trait UrlLike[T] { 

trait UrlLike[+T] { 
2

对于任何隐含的不确定性,该rule is (since Scala2.8)

当比较重载方法或隐式方法的两种不同适用方法:每种方法:

  • 得到一分具有更SPECI音响Ç参数
  • 和另一点为被德网络连接在一个适当的子类定义

如果在这两次比较中获得更多的点数,另一种方法是“胜过”另一种。
这尤其意味着如果选项具有相同的参数类型,则在子类中定义的参数将获胜。

我不认为围绕URLURI的implicits得到不同以下这些标准点。

2

从您看到的错误报告中可能不会很明显,但在对象UrlLike中定义的所有三个隐含都会导致模糊(例如,尝试注释uri的定义,您会看到歧义报道为str和url之间)。

不明确的原因是UrlSupport.url的类型参数T仅受限于有一个隐式UrlLike实例可用于它的要求。由于三个隐式对象提供的UrlLike实例,字符串,URL和URI都同样满足该要求。编译器不会随意为你选择其中的一个,所以它报告含糊不清。

除非你明确指定类型参数来解决歧义,就像你在最后两次REPL交互中所做的那样。

+0

编译器是否看不到由于类型归属而导致String/URL/URI的结果? – IttayD 2011-03-22 14:45:56

5

我可以看到为什么你会期望它的工作,但显然,类型推理不使用返回类型来推断T。我也期待它。

至于含糊不清,CanBuildFrom避免由于没有定义在同一“级别”的所有内容而造成含糊不清。举例来说,这解决了模糊问题:

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

但是,它将使类型推断工作,你所希望的方式:这表明它显然使得T推理没有考虑到

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

考虑返回类型。