内在特征打破了隐含参数

时间:2013-04-02 11:55:52

标签: scala implicit

object Test {

  trait Foo

  trait TC[A]

  object TC {
    implicit def tc1[F <: Foo] = new TC[F] {}
    implicit def tc2[F1 <: Foo, F2 <: Foo] = new TC[(F1, F2)] {}
  }   

  object Bar {
    trait X
    val c = new Foo with X
    def bar[A](a: this.type => A)(implicit tc: TC[A]) = 1
  }

  Bar bar (_.c)

  Bar bar (b => (b.c, b.c))
}

最后一行给出了编译器错误“找不到参数tc的隐式值...”。

现在:在trait X之外移动object Bar使其有效。

最后一行的下一行适用于两种情况。

这有什么好的理由,和/或是否可以在不将特性移出物体的情况下使其工作?

1 个答案:

答案 0 :(得分:2)

这不是一个完整的答案(并且包含大量的猜测),但鉴于你到目前为止我没有,我想它总比没有好。

似乎问题在于编译器将X中的new Foo with X视为路径依赖类型,即使我们在对象定义而不是类中。 因此,编译器会在第二次调用A = (Test.Foo with b.X, Test.Foo with b.X) forSome { val b: Test.Bar.type }时推断出bar。 这需要编译器找到类型为TC[(Test.Foo with b.X, Test.Foo with b.X) forSome { val b: Test.Bar.type }]的隐式值,显然tc2是不合适的(我对scala类型系统的极端情况不够了解,以确定是否存在真正的不兼容,或者如果编译器没有线索的话)

[推测模式]

与路径相关的类型处理的问题闻起来像是一个错误(或至少是一个不明确的怪异)。我认为罪魁祸首是一个对象的主体编译与一个类没有什么不同,然后以某种方式被制成一个单独的对象,这意味着new Foo with X实际上被视为{ {1}}而不是new Foo with this.X,因此被视为路径依赖类型(即使他们应该在这种情况下恕我代表相同的事情)。

[/推测模式]

现在对于奇怪的部分(这也是你要求的工作):转过来:

new Foo with Bar.X

进入这个:

val c = new Foo with X

实际上修复了编译。据我了解,这是因为通过显式指定val c = new Foo with Bar.X ,我们强制编译器识别Bar是一个稳定的路径,这清除了路径相关类型的问题。

现在,真正的解决方法当然是按照您的建议将Bar.X移到X之外。它有效且无痛,所以为什么不这样做呢?