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
implicit
at 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 betweenfinder1
andfinder2
precisely in following code:Sidenote: the fact that some parameter is
implicit
doesn'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
finder1
orfinder2
might be assigned in this example is justBroFinder1[Foo]
. Particularly there is no way to catch different values of theb
implicit 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 ofbof1
andbof2
is that they both have type in form of_1.b.Bro
where_1
means some particular instance ofBroFinder1[Foo]
. And so the compiler can't be sure thatbof1
is actually of typeBar
whilebof2
is of typeFoo
.The second example works because implicit parameter is captured in the same context so compiler knows exactly what the result type of
brotherOf
is.The only workaround I know is not exactly suitable in your case: use "Aux" type. If your
BroFinder
took also an explicit parameter of typeFoo
, the solution might have looked like this:But in your case it has to be much less helpful