让我们假设我有一个任意单参数泛型类的实例(我在演示中使用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]
而不是List
而Nothing
1}}而不是Int
代表第3名。
我认为我提出的foo
的类型界限不够精确/正确,所以我的问题是如何实现它,因此Scala可以推断出参数?
注意:如果有帮助,可以假设所有潜在的泛型(A
s)都可以继承/符合一些共同的祖先。例如,A
可以是从Seq
继承的任何集合。
注意:这个问题中描述的例子是合成的,是我试图解决的更大问题的一部分。
答案 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
。
更新(根据评论)。
如果您需要证据C
是A[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])