对不可变集合使用上限

Using upper-bounds for immutable collections

我正在尝试创建一个方法,该方法可以采用 immutable.Seq 的任何后代的类型参数。这是我到目前为止得到的:

def writeSomeData[Holder[_] <: Seq[String]](path: String, holder: Holder[String]): Unit = {
// irrelevant implementation
  }

然而,当我使用 immutable.List:

调用上述方法时
writeSomeData(tmp, List(res1, res2, res3, res4, res5))

它因以下错误而中断:

[error] /home/v.gorcinschi/repos/...: inferred type arguments [List] do not conform to method writeSomeData's type parameter bounds [Holder[_] <: Seq[String]]
[error]       writeSomeData(tmp, List(res1, res2, res3, res4, res5))
[error]       ^
[error] /home/v.gorcinschi/repos/...: type mismatch;
[error]  found   : List[String]
[error]  required: Holder[String]
[error]       writeSomeData(tmp, List(res1, res2, res3, res4, res5))

为什么会发生这种情况,我该如何纠正? List 是 Seq 的后代,不是吗?

问题是 Holder[_] <: Seq[String] 并不像您认为的那样。我实际上不确定它是什么意思,可能类似于:trait Holder[A] extends Seq[String],如您所见,它与 List 的外观非常不同。

你实际上想表达 Holder[String] 应该是 Seq[String] 的子类型 许多人认为正确的语法是 Holder[_] <: Seq[_] 但是,这也是不正确的;这只是意味着 Holder 必须扩展 Seq 但不保证 Holder[String] <: Seq[String]
值得庆幸的是,Scala 确实提供了语法来表示:Holder[x] <: Seq[x] x 不是另一个类型参数,只是表示 Holder[x] 是的一种方式Seq[x] 的任何类型 x

的子类型

有时,使用广义类型约束也更容易:

def foo[Holder[_]](holder: Holder[String])(implicit ev: Holder[String] <:< Seq[String]): Unit

代表完全相同的关系。


然而,只有当你在 return 类型中引用 Holder 时才有用,如果你只想使用任何 Seq 那么你可以这样做:

def foo(holder: Seq[String]): Unit

感谢 Liskov,您可以在那里传递 Seq[String] 的任何子类型。

类型约束

Holder[_] <: Seq[String]

means 方法采用任何类型构造函数 Holder,使得 Holder[X]Seq[String] 的子类型,用于任意 X。任意我们的意思是 X >: Nothing <: Any.

现在,当您将 List("") 作为参数传递时,Scala 将尝试统一

?Holder := List

这样 Holder[X] <: Seq[String] 任意 X。但是 List[X] 不是 Seq[String] 的子类型,对于任意 X,例如 X := Int。因此它不进行类型检查。

作为解决方法,我们可以做

Holder[x <: String] <: Seq[x]

这有效地实现了同样的事情,但这次它起作用了,因为现在

Holder[x] <: Seq[x]

确实适用于任意 x 因为 ListSeq 的定义相似

trait Seq[+A]
trait List[+A]

对于任意 x.

[X] =>> List[X] <: [X] =>> Seq[X] 是真的

Plus 编译器有来自 Holder[x <: String] 的额外信息 x <: String 来检查参数的元素类型。

给定

Holder[_] <: Seq[String]

您可以通过显式传递适当类型的 lambda 来帮助编译器使其工作,例如

writeSomeData[[X] =>> List[String]](???, List(""))

但是我认为 Scala 不会自动推断出这种类型的 lambda,因为通常类型构造函数统一是 undecidable.