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
使其有效。
最后一行的下一行适用于两种情况。
这有什么好的理由,和/或是否可以在不将特性移出物体的情况下使其工作?
答案 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
之外。它有效且无痛,所以为什么不这样做呢?