编译器在BroFinder1
和BroFinder2
中看到哪些导致第一个失败的区别?我确实需要BroFinder1
才能正常工作,而不使用Aux Pattern等模式。
trait Brother[I] {
type Bro
def get: Bro
}
class Foo
object Foo {
implicit object bro extends Brother[Foo] {
override type Bro = Bar
override def get = new Bar
}
}
class Bar {
def barSpecificAction(): Unit = ()
}
class BroFinder1[I](implicit val b: Brother[I]) {
def brotherOf: b.Bro = b.get
}
class BroFinder2[I] {
def brotherOf(implicit b: Brother[I]): b.Bro = b.get
}
new BroFinder1[Foo].brotherOf.barSpecificAction() // Doesn't compile
//error: Error:(73, 32) value barSpecificAction is not a member of _1.b.Bro
//new BroFinder1[Foo].brotherOf.barSpecificAction();
^
new BroFinder2[Foo].brotherOf.barSpecificAction() // Ok
//scala version: 2.12.4
答案 0 :(得分:3)
这不是一个完美的答案,但可能会提供一些见解。该问题似乎与implicit
无关。它似乎与另一个相当先进的Scala功能有关: 路径依赖类型 。特别是麻烦似乎来自这样一个事实:Scala类型系统不够强大,无法在以下代码中精确表达finder1
和finder2
之间的类型差异:
object Foo {
implicit object bro extends Brother[Foo] {
override type Bro = Bar
override def get = new Bar
}
object bro2 extends Brother[Foo] {
override type Bro = Foo
override def get = new Foo
}
}
val finder1 = new BroFinder1[Foo]
val finder2 = new BroFinder1[Foo]()(Foo.bro2)
val bof1 = finder1.brotherOf
val bof2 = finder2.brotherOf
Sidenote :某些参数为implicit
的事实并不会使其成为“常量”,因为您始终可以显式传递参数或者您在不同的上下文中可以有不同的可见隐含值。
此示例中可能分配finder1
或finder2
的最精确类型的AFAIU仅为BroFinder1[Foo]
。特别是没有办法捕获b
隐式变量的不同值,因此无法进一步准确传递在该值内编码的路径相关类型的值。因此,最好的编译器知道bof1
和bof2
的类型,它们都具有_1.b.Bro
形式的类型,其中_1
表示BroFinder1[Foo]
的某个特定实例。因此,编译器无法确定bof1
实际上是Bar
类型,而bof2
类型为Foo
。
第二个示例有效,因为隐式参数是在同一个上下文中捕获的,因此编译器确切知道brotherOf
的结果类型是什么。
我知道的唯一解决方法并不完全适用于您的情况:使用“ Aux ”类型。如果您的BroFinder
也采用类型为Foo
的显式参数,则解决方案可能如下所示:
type BrotherAux[I, B] = Brother[I] {type Bro = B}
class BroFinder3[I, B](val v: I)(implicit val b: BrotherAux[I, B]) {
def brotherOf: B = b.get
}
new BroFinder3(new Foo).brotherOf.barSpecificAction()
但在你的情况下,它必须没那么有用
class BroFinder1[I, B](implicit val b: BrotherAux[I, B]) {
def brotherOf: B = b.get
}
new BroFinder1[Foo, Bar].brotherOf.barSpecificAction()
答案 1 :(得分:3)
因为new BroFinder2[Foo].brotherOf
的类型为Bar
而Bar
的方法为barSpecificAction
,但new BroFinder1[Foo].brotherOf
属于存在类型
x.Bro forSome { val x: Brother[Foo] }
并且它没有方法barSpecificAction
。