Scala:泛型的类型推断及其类型参数

时间:2014-09-17 10:56:55

标签: scala generics variance

让我们假设我有一个任意单参数泛型类的实例(我在演示中使用List但这可以是任何其他通用的。)

我想编写可以接受实例(c)并能够理解泛型类(A)和类型参数(B)的泛型函数产生了该实例的类(C)。

我已经想出了类似的东西(函数的主体并不真正相关,但证明C符合A[B]):

def foo[C <: A[B], A[_], B](c: C) {
  val x: A[B] = c
}

...如果你这样调用它就会编译:

foo[List[Int], List, Int](List.empty[Int])

...但如果我省略显式类型参数并依赖推理,则编译失败并出现错误:

foo(List.empty[Int])

我得到的错误是:

    Error:Error:line (125)inferred kinds of the type arguments (List[Int],List[Int],Nothing) do not conform to the expected kinds of the type parameters (type C,type A,type B).
List[Int]'s type parameters do not match type A's expected parameters:
class List has one type parameter, but type A has one
  foo(List.empty[Int])
  ^
    Error:Error:line (125)type mismatch;
 found   : List[Int]
 required: C
  foo(List.empty[Int])
                ^

正如您所看到的,在这种情况下,Scala的类型推断无法正确推断出类型(对于第二个参数而言,它似乎是List[Int]而不是ListNothing 1}}而不是Int代表第3名。

我认为我提出的foo的类型界限不够精确/正确,所以我的问题是如何实现它,因此Scala可以推断出参数?

注意:如果有帮助,可以假设所有潜在的泛型(A s)都可以继承/符合一些共同的祖先。例如,A可以是从Seq继承的任何集合。

注意:这个问题中描述的例子是合成的,是我试图解决的更大问题的一部分。

2 个答案:

答案 0 :(得分:3)

这是类型构造函数的当前Scala类型推断的已知限制。将形式参数c的类型定义为C仅收集C的类型约束(并间接收集到A)但不收集B.换句话说List[Int] <: C => { List[Int] <: C <: Any, C <: A[_] <: Any }

有一个非常简单的翻译,允许指导这种情况的类型推断。在你的情况下它是:

def foo[C[_] <: A[_], A[_], B](c: A[B]) { val x: A[B] = c }

相同的语义,只是略有不同的类型签名。

答案 1 :(得分:1)

除了 hubertp 回答之外,你可以通过删除过时的(在你的例子中)类型变量C来修复你的功能,例如:

def foo[A[_], B](c: A[B]) {
  val x: A[B] = c
}

在这种情况下,scalac会将A[_]推断为List,将B推断为Int

更新(根据评论)。

如果您需要证据CA[B]的子类型,请使用隐式:

def foo[A[_], B, C](c: C)(implicit ev: C <:< A[B]) = {
  val x: A[B] = c
}

然后它不会编译这个:

scala> foo[List, String, List[Int]](List.empty[Int])
<console>:9: error: Cannot prove that List[Int] <:< List[String].
              foo[List, String, List[Int]](List.empty[Int])