In your original question, your problem is that the Scala compiler is unable to prove equality of the result type of foo.makeA
with the argument type of fooWrap.a.useA
. To do that it would need to be able to prove the identity of foo
with fooWrap.a
which we can intuitively see must be the case here, but which isn't straightforward for the compiler to track.
There are a couple of ways to work around this problem. First, you could use fooWrap.a
uniformly in place of foo
,
scala> fooWrap.a.useA(fooWrap.a.makeA)
1
Now it's simple for the compiler to recognize the prefix of A (fooWrap.a
) as being the same in both occurrences.
Second, you could parametrize FooWrap
in a way which captures the type of its Foo
argument more precisely,
scala> class FooWrap[F <: Foo](val a: F) {
| def wrapUse(v: a.A) = a.useA(v)
| }
defined class FooWrap
scala> val fooWrap = new FooWrap(foo)
fooWrap: FooWrap[IntFoo] = FooWrap@6d935671
scala> fooWrap.a.useA(foo.makeA)
1
Here the type argument of FooWrap
is inferred as IntFoo
rather than as bare Foo
, hence A
is known to be exactly Int
, as it is in the result type of foo.makeA
.
In your update you introduce an additional wrinkle: you change the signature of useA
to,
def useA(a: A[_]): Unit
The _
here is an existential which will frustrate all attempts to coax the compiler into proving useful type equalities. Instead you need something along the lines of,
trait Foo {
type A[T]
def makeA[T]: A[T]
def useA[T](a: A[T]): Unit
}
class OptFoo extends Foo {
type A[T] = Option[T]
def makeA[T]: A[T] = None
def useA[T](a: A[T]) = a map println
}
class FooWrap[F <: Foo](val a: F) {
def wrapUse[T](v: a.A[T]) = a.useA(v)
}
val foo = new OptFoo
Sample REPL session,
scala> val fooWrap = new FooWrap(foo)
fooWrap: FooWrap[OptFoo] = FooWrap@fcc10a7
scala> fooWrap.a.useA(foo.makeA)
scala>