What difference does the compiler see in BroFinder1 and BroFinder2 that causes the first one to fail? I really need BroFinder1 to work as it is without utilizing patterns such as 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
This is not a perfect answer but probably will provide some insights. The issue seems to be not related to
implicitat all. It seems to be related to another fairly advance Scala feature: path-dependent types. Particularly the trouble seems to come from the fact that Scala type system is not powerful enough to express type difference betweenfinder1andfinder2precisely in following code:Sidenote: the fact that some parameter is
implicitdoesn't make it a "constant" because you can always pass the parameter explicitly anyway or you can have different visible implicit values in different contexts.AFAIU the most precis type the
finder1orfinder2might be assigned in this example is justBroFinder1[Foo]. Particularly there is no way to catch different values of thebimplicit variable and thus there is no way to pass further exactly the value of the path-dependent type that is encoded inside that value. Thus the best compiler knows about the types ofbof1andbof2is that they both have type in form of_1.b.Browhere_1means some particular instance ofBroFinder1[Foo]. And so the compiler can't be sure thatbof1is actually of typeBarwhilebof2is of typeFoo.The second example works because implicit parameter is captured in the same context so compiler knows exactly what the result type of
brotherOfis.The only workaround I know is not exactly suitable in your case: use "Aux" type. If your
BroFindertook also an explicit parameter of typeFoo, the solution might have looked like this:But in your case it has to be much less helpful