Reputation: 1313
Can't figure out why is this wrong or how to resolve it. Here's "distilled" code that reproduces the problem. Please help, but I'd appreciate none of the "why" questions - there are very real and valid answers to that but they are proprietary and unchangeable, thus irrelevant to the solution.
object Sandbox {
// --- Following are really Java interfaces/classes ---
trait R[X <: U[X,Y], Y <: E[X,Y]];
class U[X <: U[X,Y], Y <: E[X,Y]] extends R[X,Y];
class E[X <: U[X,Y], Y <: E[X,Y]] extends R[X,Y];
trait R2 extends R[U2,E2];
class U2 extends U[U2,E2] with R2;
class E2 extends E[U2,E2] with R2;
// --- End Java interfaces/classes ---
def trouble[X <: U[X,Y], Y <: E[X,Y], Z <: R[X,Y]](r: Z) {}
def main(args: Array[String]) {
trouble(new U()); // Fine
trouble(new E()); // Fine
trouble(new U2()); // Not fine, reports:
/*
* inferred type arguments [Nothing,Nothing,Sandbox.U2]
* do not conform to method trouble's type parameter bounds
* [X <: Sandbox.U[X,Y],Y <: Sandbox.E[X,Y],Z <: Sandbox.R[X,Y]]
*/
trouble(new E2()); // Not fine, reports:
/*
* inferred type arguments [Nothing,Nothing,Sandbox.E2]
* do not conform to method trouble's type parameter bounds
* [X <: Sandbox.U[X,Y],Y <: Sandbox.E[X,Y],Z <: Sandbox.R[X,Y]]
*/
trouble[U2,E2,R2](new U2()); // Fine
trouble[U2,E2,R2](new E2()); // Fine
}
}
The compiler can't seem to infer X, Y and Z type args of the "trouble" method just based on the single argument specified. I understand that much - when I specify the types, it is OK, but it is very cumbersome. Is there a way to nudge/help the compiler in some way such that this stops being a problem?
Maybe I am placing too much confidence in Scala's type inference system, but all the information is available to it.
Thanks in advance!
Upvotes: 3
Views: 785
Reputation: 139038
See this answer (and the answers I've linked there) for a discussion of the limitations of Scala's type inference that are making a mess of things here.
If you don't need X
and Y
in the body (or return type) of trouble
, you can use existential types to avoid referring to them at all:
def trouble[Z <: R[_, _]](r: Z) {}
If you do need them, you can use a view bound:
def trouble[X <: U[X, Y], Y <: E[X, Y], Z <% R[X, Y]](r: Z) {}
See the answers linked above for an explanation of why this works.
Upvotes: 1
Reputation: 11366
You are placing too much confidence in Scala's type system inference. The more you try to work with these more complex (and especially recursive) type definition, the more you'll discover this. I don't know if I can offer a "why can't it figure this out" but I can offer something that works:
Don't parameterize the R on the types, but make them abstract members which must be declared in subtypes:
trait R {
type X <: U[X,Y]
type Y <: E[X,Y]
}
class U[X0 <: U[X0,Y0],Y0 <: E[X0,Y0]] extends R {
type X = X0
type Y = Y0
}
class E[X0 <: U[X0,Y0], Y0 <: E[X0,Y0]] extends R {
type X = X0
type Y = Y0
}
trait R2 extends R;
class U2 extends U[U2,E2] with R2
class E2 extends E[U2,E2] with R2
def trouble[X <: U[X,Y], Y <: E[X,Y], Z <: R](r: Z) {}
Then I believe you'll find your main method compiling unchanged.
As an aside, every semicolon in your code could be removed without changing the meaning.
Upvotes: 4