【问题标题】:Why does Scala warn about type erasure in the first case but not the second?为什么 Scala 在第一种情况下警告类型擦除而不是第二种情况?
【发布时间】:2011-11-01 10:25:27
【问题描述】:

我有两个函数(这些函数自原始函数以来没有被编辑过——下面的一些答案是对返回 () 序列的原始函数的响应):

def foo1[A](ls: Iterable[A]) : Iterator[A] =
    for (List(a, b) <- ls sliding 2) yield a

def foo2[A](ls: Iterable[A]) : Iterator[A] =
    for (a::b::Nil <- ls sliding 2) yield a

我天真地认为是一样的。但是 Scala 只在第一个方面给出了这种减弱:

warning: non variable type-argument A in type pattern List[A]
is unchecked since it is eliminated by erasure

我想我理解为什么它会给第一个错误:Scala 认为我正在尝试将类型用作模式的条件,即如果 B 不继承自,则与 List[B](_, _) 的匹配应该失败A,除了这不可能发生,因为在这两种情况下都会删除类型。

所以两个问题:

1) 为什么第二个没有给出同样的警告?

2) 是否有可能让 Scala 相信该类型在编译时实际上是已知的,因此不可能匹配失败?

编辑:我认为this answers my first question。但我还是对第二个很好奇。

编辑:agilesteel 在评论中提到

for (List(a, b) <- List(1,2,3,4) sliding 2) yield ()

不产生任何警告。这与foo1 有什么不同(不应该像[A] 参数一样删除[Int] 参数吗)?

【问题讨论】:

    标签: scala pattern-matching type-erasure


    【解决方案1】:

    我不确定这里发生了什么,但Iterable[A].sliding 的静态类型是Iterator[Iterable[A]],而不是Iterator[List[A]],后者是List[A].sliding 的静态类型。

    您可以尝试接收Seq 而不是Iterable,这样也可以。 编辑 与我之前声称的相反,Iterable 和 Seq 都是协变的,所以我不知道有什么不同。 END EDIT sliding 的定义也很奇怪:

    def sliding [B >: A] (size: Int): Iterator[Iterable[A]]
    

    看看它如何需要一个永远不会被使用的B,A 的超类?将其与Iterator.sliding 进行对比,这没有问题:

    def sliding [B >: A] (size: Int, step: Int = 1): GroupedIterator[B]
    

    不管怎样,进入第二种情况:

    for (a::b::Nil <- ls sliding 2) yield a
    

    在这里,您将列表分解两次,每次分解都会根据A 检查head 的类型。由于head 的类型没有被擦除,所以你没有问题。这也主要是猜测。

    最后,如果你把ls 变成List,你就不会有问题了。除此之外,我认为您无能为力。否则,你也可以这样写:

    def foo1[A](ls: Iterable[A]) : Iterator[A] =
        for (Seq(a, b) <- ls.iterator sliding 2) yield a
    

    【讨论】:

    • 哇,Scala 好复杂! (或者更确切地说,我对它的并发症非常陌生)。我原以为Iterable 会是协变的。
    • @Owen Scala 有深度,但大多数情况下,您可以选择留在浅水区。 Iterable 不能是协变的,因为 Set 不是协变的,Map 也不是。而且,事实上,你可以任意限制Iterable,使其成为协变体。
    • 当前文档(我不知道什么版本是“当前”)说 Iterable 是协变的:scala-lang.org/api/current/scala/collection/Iterable.html
    • @Owen 你是对的。我将编辑我的答案的那部分,因为它显然是错误的。我仍然不知道为什么一旦你从Iterable 跨越到Seq 事情就开始工作了。
    【解决方案2】:

    我对 Scala 不是很熟悉,但我使用过 Java 和 Haskell,所以我将在这里冒昧地猜测您的第二个示例并没有做完全相同的事情。看起来他们都使用模式匹配来解构列表的前两个元素,但第二个使用的是 cons 运算符而不是 List 构造函数。我的猜测是,这在某种程度上与与 Java 的互操作有关,以及 List 构造函数与 cons 运算符实际运行方式之间的细微差别。

    另一方面,也许这只是编译器忽略的边缘情况,它们都应该生成警告。

    【讨论】:

    • 我认为 Agilesteel 做到了。这是因为 Nil 是 List[Nothing] 的别名,所以通过扩展,第二个示例类型检查 a 和 b 作为 Nothing 的实例。这与第一个从不指定 a 和 b 类型的示例形成对比。
    • for (List(a, b) &lt;- List(1,2,3,4) sliding 2) yield () 不会像在他的方法中那样产生警告。
    • @agilesteel 现在我真的很困惑! [Int] 参数不应该和[A] 参数一样被删除吗?
    • List(a, b) 是一个完全正确的构造函数模式,a::b::Nil 也是如此。对不起,我不能再帮你了,但我开始认为这与类型擦除无关。看看this question也许会有所帮助。
    【解决方案3】:

    1) 第二个没有产生警告可能是因为您正在通过将元素添加到Nil 对象来构造列表(或模式),该对象扩展了List 并使用Nothing 对其进行参数化。而且由于一切都是Nothing,所以没有什么可担心的;)但我不确定,真的在这里猜测。

    2) 你为什么不直接使用:

    def foo[A](ls: Iterable[A]) =
        for (list <- ls sliding 2) yield ()
    

    【讨论】:

    • (2):抱歉,我实际上并没有尝试列出所有 () - 我取出了循环的实际主体,以免分散注意力。猜猜这适得其反;)
    • (1):我对 Scala 很陌生,所以我不知道这是否正确,但我尝试指定返回类型并且警告保持不变。我将编辑我的问题以显示。
    猜你喜欢
    • 2022-01-22
    • 1970-01-01
    • 2022-11-27
    • 2022-12-18
    • 1970-01-01
    • 2015-09-30
    • 2020-02-03
    • 2019-12-12
    • 2015-10-14
    相关资源
    最近更新 更多