对不可变集合使用上限
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
因为 List
和 Seq
的定义相似
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.
我正在尝试创建一个方法,该方法可以采用 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
因为 List
和 Seq
的定义相似
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.