4

如果我的名字有关斯卡拉的最讨厌的事情,这将是为下面的代码:泛型类型和类型参数的Scala类型推断 - 为什么它不起作用?

trait G[+T] 
class H[+T] extends G[T] 

def f[A<:G[X], X<:Int](g :A) 

val g :H[Int] 
f(g) 

编译器推断类型F中的最后一次通话[H [INT],没什么]和在我面前抱怨自己的愚蠢。

但是知道scala,它实际上比我知道得更清楚。它背后的原因是什么?由于G和H对于T来说是协变的,所以对于任何类型S来说都是S <: G[X] with H[_] <=> S<: H[X]。这一缺点使我设计了一切,避免明确地指定类型 - 它可能看起来像什么都没有,但是当名称变成'真实'长度和几乎任何方法都是泛型的,并且通常在两个泛型类型上工作,事实证明,大多数代码都是类型声明。

编辑: 上面的情况是由诺亚解决的,但是当派生类与基类不同,如下所示?

trait G[+X] 
class H[+X, Y] extends G[X] 
class F extends G[Int] 
def f[A<:G[X], X<:Int](g :A) = g 

val h: H[Int, String] = ??? 
val g :F = ??? 
f(g) 
f(h) 

回答

3

如果您A采取型paramater A[_]我认为你可以得到Scala编译器同意你的观点,而不是仅仅将一切都Nothing的:

def f[A[_] <: G[_], X <: Int](g: A[X]) 

作为一个方面说明,我通常采取每当我遇到类型问题时,都会看到scalaz源代码,因为他们通常会遇到它并尽可能地解决问题。

UPDATE

我上面提供的仍然给出的附加约束的工作方法:

trait G[+X] 

    class H[+X, Y] extends G[X] 

    class F extends G[Int] 

    class I extends G[String] 

    def f[A[_] <: G[_], X <: Int](g: A[X]) = g 

    val h: H[Int, String] = new H[Int, String] 
    val g: F = new F 
    val i:I = new I 
    f(g) //works 
    f(h) //works 
    f(i) // should fail and does fail 
+0

谢谢,它有一定的帮助,看起来像在这里circumvenes问题。但是,如果H是另一种类型,它将无济于事 - 需要更多的类型参数或没有,从而实例化G的参数。或者如果H在其参数上引入界限。所以我仍然对如果上述不起作用的理由感兴趣。 – Turin

+0

你必须给出另一个不起作用的例子,我敢肯定,你通常可以让编译器同意你的看法。上面发生的事情是,编译器无法从'g:A'中提取类型。它可以发现'A'是'H [Int]',但即使你说'A <:G [X]',你也没有指定'X',所以它会成为barf。在我给出的例子中,指定了类型'X',以便编译器可以推断它。 – Noah

+0

增加了解释更实际案例的代码。 – Turin