You have several options. One of them is to explicitly specify type parameters:
scala> fill[A, A](new A, new P) Am res1: A = A@4beb8b21
If method m always returns the value of the type in which it is defined, you can help with the type inference by encoding this fact in your fill :
scala> def fill[T <: WithM[T]](o: T, p: P): T = om(p) fill: [T <: WithM[T]](o: T, p: P)T scala> fill(new A, new P) Am res2: A = A@5f9940d4
You can also skip an alias like:
scala> def fill[S](o: { def m(o: P): S }, p: P): S = om(p) fill: [S](o: AnyRef{def m(o: P): S}, p: P)S scala> fill(new A, new P) Am res3: A = A@3388156e
I would highly recommend using a type class, although these are slightly syntactic utility ones, but much cleaner:
trait HasM[T] { type Out def apply(t: T, p: P): Out } object HasM { type Aux[T, Out0] = HasM[T] { type Out = Out0 } implicit def AHasM: Aux[A, A] = new HasM[A] { type Out = A def apply(t: A, p: P): A = tm(p) } implicit def BHasM: Aux[B, B] = new HasM[B] { type Out = B def apply(t: B, p: P): B = tm(p) } } def fill[T](t: T, p: P)(implicit hm: HasM[T]): hm.Out = hm(t, p)
And then:
scala> fill(new A, new P) Am res4: A = A@74e92aa9 scala> fill(new B, new P) Bm res5: B = B@1ea35068
There is no reflexive access, and you are using a widely understood idiom.
Travis brown
source share