2012-04-27 34 views
6

这个问题出现在我正在编写的模块中,但我做了一个展示相同行为的最小案例。为什么不在这里输入推理?

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 
} 

的问题是,编译器设法计算出用于MinimalInt)内的参数,但然后设置的T其它发生于Nothing,这显然不匹配apply。这些肯定是相同的T,因为删除第一个参数使第二个抱怨T未定义。

是否存在一些含糊不清的含义,即编译器无法推断出第一个参数,或者这是一个错误?我能优雅地解决这个问题吗?

更多信息:此代码是一个尝试语法糖的简单示例。原始代码试图使|(a)|表示模数a,其中a是一个向量。很明显,|(a)|比编写|[Float,Vector3[Float]](a)|要好,但不幸的是我不能使用unary_|来使这更容易。

实际的错误:

inferred type arguments [Nothing,Minimal[Int]] do not conform to method apply's type parameter bounds [T,X <: Sugar.S[T]]

回答

9

这不是一个Scala编译器错误,但它当然是Scala类型推断的限制。编译器想要在求解X之前确定X,上的边界,但是边界提到了迄今为​​止无约束的类型变量T,因此它在Nothing处修正并从那里继续。一旦X已完全解决,它不会重新访问T ...目前,在这种情况下,类型推断总是从左到右继续。

如果您的示例准确代表你的真实情况然后有一个简单的解决,

def apply[T](x : S[T]) = x.doSomething 

这里T将推断使得Minimal符合S[T]直接而不是通过中间有界类型的变量。

更新

约书亚的解决方案也避免了类型推断的T的问题,但在一个完全不同的方式。

def apply[T, X <% S[T]](x : X) = x.doSomething 

desugars到,

def apply[T, X](x : X)(implicit conv : X => S[T]) = x.doSomething 

的类型变量TX现在可以解决的,用于独立地(因为TX的结合不再提及)。这意味着X被立即推断为Minimal,并且T被解决为隐式搜索X => S[T]类型的值以满足隐式参数conv的一部分。 conformsscala.Predef制造这种形式的值,并在上下文将保证给定的参数类型Minimal,T将被推断为诠释。您可以将其视为在斯卡拉工作的functional dependencies的实例。

+0

是的,这个解决方案在我的情况下工作正常。它比约书亚的解决方案更清洁(对不起约书亚!)。你能解释为什么约书亚的解决方案有效吗? – Dylan 2012-04-27 18:37:43

+0

答案更新,以解释Joshua解决方案的原因。 – 2012-04-27 20:05:49

4

有一些古怪与结构类型界限,尝试使用结合在S [T]代替的图。

def apply[T, X <% S[T]] (x: X) = x.doSomething工作正常。

+1

伟大的,这个工程,但肯定应该没有区别的方法?在这种情况下采取一种观点只是投向一个超类,不是吗?这是否意味着此修复只是解决编译器中的错误的一种解决方法? – Dylan 2012-04-27 01:22:53

+0

啊,现在我明白了,'S'不是超类 - 它只是一种观点(从某种意义上说)。因此,尽管在大多数情况下并不需要,但观点更合适。 – Dylan 2012-04-27 18:09:36