简体   繁体   中英

Why doesn't type inference work here?

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]]

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM