【问题标题】:Using upper-bounds for immutable collections使用不可变集合的上限
【发布时间】:2021-09-05 08:14:15
【问题描述】:

我正在尝试创建一个可以采用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 声明为高阶类型,但设置了非高阶类型的界限,因此它永远不会匹配。 Luis 的解决方案是正确的

标签: scala types liskov-substitution-principle


【解决方案1】:

问题是Holder[_] &lt;: Seq[String] 并不代表你认为的那样。我实际上不确定它是什么意思,可能类似于:trait Holder[A] extends Seq[String],正如您所看到的,它与 List 的样子非常不同。

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

有时,您还可以更轻松地使用通用类型约束,例如:

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

表示完全相同的关系。


不过,只有在返回类型中引用 Holder 时才有用,如果您只想使用任何 Seq,那么您可以这样做:

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

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

【讨论】:

    【解决方案2】:

    类型约束

    Holder[_] <: Seq[String]
    

    表示方法采用任何类型的构造函数Holder,使得Holder[X] 是任意XSeq[String] 的子类型。任意指的是X &gt;: Nothing &lt;: Any

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

    ?Holder := List
    

    这样Holder[X] &lt;: Seq[String] 代表任意X。但是List[X] 不是Seq[String] 的子类型,用于任意X,例如采用X := Int。因此它不输入检查。

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

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

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

    Holder[x] &lt;: Seq[x]

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

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

    确实[X] =&gt;&gt; List[X] <:>[X] =>> Seq[X] 为任意x

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

    给定

    Holder[_] <: Seq[String]
    

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

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

    但我认为 Scala 不会自动推断此类 lambda,因为通常类型构造函数统一为 undecidable

    【讨论】:

    • 如果写Holder[x &lt;: String] &lt;: Seq[x],是不是表示Holder的上限是Seq类型构造函数而不是Seq[String]的完整类型?这与Holder[x &lt;: String] &lt;: Seq[String] 不同。如果你这样做,它仍然不会匹配
    • @texasbruce 否上限&lt;: Seq[x] 不是类型构造函数。
    • 我的意思是,如果你的理论是正确的,并且我们对 Holder 的类型参数施加类型约束,它应该可以工作,因为List[String] 会然后&lt;:&lt; Holder[x] 因为x &lt;: String,但它没有
    • @texasbruce 不确定我是否理解,它似乎在这里工作scastie.scala-lang.org/Pe2sVGE1TfKgWyvcW2Ne7A
    • 那是因为你有Seq[x] 而不是Seq[String]。根据你的理论,这应该可以def f[Holder[_ &lt;: String] &lt;: Seq[String]](holder: Holder[String]) 但它没有
    猜你喜欢
    • 2016-02-02
    • 2021-01-12
    • 1970-01-01
    • 2012-01-19
    • 2011-03-11
    • 1970-01-01
    • 2012-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多