问题
This problem arose in a module I'm writing, but I have made a minimal case that exhibits the same behaviour.
class Minimal[T](x : T) {
def doSomething = x
}
object Sugar {
type S[T] = { def doSomething : T }
def apply[T, X <: S[T]] (x: X) = x.doSomething
}
object Error {
val a = new Minimal(4)
Sugar(a) // error: inferred [Nothing, Minimal[Int]] does not fit the bounds of apply
Sugar[Int, Minimal[Int]](a) // works as expected
}
The problem is that the compiler manages to figure out the inner parameter for Minimal
(Int
), but then sets the other occurrence of T
to Nothing
, which obviously does not match apply
. These are definitely the same T
, as removing the first parameter makes the second complain that T is not defined.
Is there some ambiguity that means that the compiler cannot infer the first parameter, or is this a bug? Can I work around this gracefully?
Further information: This code is a simple example of an attempt at syntactic sugar. The original code tries to make |(a)|
mean the modulus of a
, where a is a vector. Clearly |(a)|
is better than writing |[Float,Vector3[Float]](a)|
, but unfortunately I can't use unary_|
to make this easier.
The actual error:
inferred type arguments [Nothing,Minimal[Int]] do not conform to method apply's type parameter bounds [T,X <: Sugar.S[T]]
回答1:
This isn't a Scala compiler bug, but it's certainly a limitation of Scala's type inference. The compiler wants to determine the bound on X
, S[T]
, before solving for X
, but the bound mentions the so far unconstrained type variable T
which it therefore fixes at Nothing
and proceeds from there. It doesn't revisit T
once X
has been fully resolved ... currently type inference always proceeds from left to right in this sort of case.
If your example accurately represents your real situation then there is a simple fix,
def apply[T](x : S[T]) = x.doSomething
Here T
will be inferred such that Minimal
conforms to S[T]
directly rather than via an intermediary bounded type variable.
Update
Joshua's solution also avoids the problem of inferring type T
, but in a completely different way.
def apply[T, X <% S[T]](x : X) = x.doSomething
desugars to,
def apply[T, X](x : X)(implicit conv : X => S[T]) = x.doSomething
The type variables T
and X
can now be solved for independently (because T
is no longer mentioned in X
's bound). This means that X
is inferred as Minimal
immediately, and T
is solved for as a part of the implicit search for a value of type X => S[T]
to satisfy the implicit argument conv
. conforms
in scala.Predef
manufactures values of this form, and in context will guarantee that given an argument of type Minimal
, T
will be inferred as Int. You could view this as an instance of functional dependencies at work in Scala.
回答2:
There's some weirdness with bounds on structural types, try using a view bound on S[T] instead.
def apply[T, X <% S[T]] (x: X) = x.doSomething
works fine.
来源:https://stackoverflow.com/questions/10343244/why-doesnt-type-inference-work-here