Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 325
Dylan Avatar asked Apr 27 '12 00:04

Dylan


People also ask

Does Java support type inference?

Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable.

Why is type inference good?

In some cases type inference can be easier to read and understand because it is simpler than explicit type specification.

What is type inference in Swift?

If you don't specify the type of value you need, Swift uses type inference to work out the appropriate type. Type inference enables a compiler to deduce the type of a particular expression automatically when it compiles your code, simply by examining the values you provide.


1 Answers

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.

like image 196
Miles Sabin Avatar answered Sep 20 '22 05:09

Miles Sabin