40

@uncheckedVariance可用于弥合 Scala 的声明站点差异注释和 Java 的不变泛型之间的差距。

scala> import java.util.Comparator    
import java.util.Comparator

scala> trait Foo[T] extends Comparator[T]
defined trait Foo

scala> trait Foo[-T] extends Comparator[T]     
<console>:5: error: contravariant type T occurs in invariant position in type [-T]java.lang.Object with java.util.Comparator[T] of trait Foo
       trait Foo[-T] extends Comparator[T]
             ^

scala> import annotation.unchecked._    
import annotation.unchecked._

scala> trait Foo[-T] extends Comparator[T @uncheckedVariance]    
defined trait Foo

这表示 java.util.Comparator 自然是逆变的,即类型参数T出现在参数中,而从不出现在返回类型中。

这就提出了一个问题:为什么它还用于不从 Java 接口扩展的 Scala 集合库中?

trait GenericTraversableTemplate[+A, +CC[X] <: Traversable[X]] extends HasNewBuilder[A, CC[A] @uncheckedVariance]

此注释的有效用途是什么?

4

3 回答 3

29

问题是 GenericTraversableTemplate 被使用了两次:一次用于可变集合(其类型参数应该是不变的),一次用于不可变集合(其中协方差始终为王)。

GenericTraversableTemplate 的类型检查假设 A 类型参数的协方差或不变性。但是,当我们以可变特征继承它时,我们必须选择不变性。相反,我们希望不可变子类中的协方差。

由于我们无法抽象 GenericTraversableTemplate 中的方差注释(但 ;-)),因此我们可以根据子类将其实例化为任何一个,我们必须求助于强制转换(@uncheckVariance 本质上是一种类型转换) . 为了进一步阅读,我推荐我的论文(对不起;-))或我们最近的bitrot 论文

于 2010-03-16T13:36:49.313 回答
8

在我的论文中,我描述了一种微积分 Scalina,它具有边界和方差注释作为类型语言的一部分(早期版本也可作为研讨会论文获得)。与此讨论相关的是我想在开发此微积分时采取的下一步:在此之上构建另一个层,以便您可以抽象出边界(简单)和方差注释(让我头晕目眩)。实际上,您不会只是在其中添加 1 个额外的层,而是将您的多态性构造泛化,以便它们在所有级别上工作,并将您的“属性”(边界、方差注释、所需的隐式参数......)变成常规类型有特殊的种类,它们都是抽象的。

Edsko de Vries 在唯一性类型的背景下很好地解释了“属性是类型”的想法。

Uniqueness Typing Simplified,Edsko de Vries、Rinus Plasmeijer 和 David Abrahamson。在 Olaf Chitil、Zoltán Horváth 和 Viktória Zsók(编辑):IFL 2007,LNCS 5083,第 201-218 页,2008 年。

摘要:我们提出了一个唯一性类型系统,它比 Clean 的唯一性系统和我们之前提出的系统都简单。新类型系统易于实现并添加到现有编译器中,并且可以使用高级功能(例如更高级别的类型和不可预测性)轻松扩展。我们在 Morrow 中描述了我们的实现,这是一种具有这两个特性的实验性功能语言。最后,我们证明了核心类型系统在按需调用 lambda 演算方面的稳健性。

于 2010-03-17T09:47:40.623 回答
5

我发现另一个使用 @uncheckedVariance 的地方——返回抽象类型参数的默认值的合成方法:

M:\>scala -Xprint:typer -e "class C { def p[T >: Null](t: T = null) = t }"
[[syntax trees at end of typer]]// Scala source: (virtual file)
package <empty> {
  final object Main extends java.lang.Object with ScalaObject {
    def this(): object Main = {
      Main.super.this();
      ()
    };
    def main(argv: Array[String]): Unit = {
      val args: Array[String] = argv;
      {
        final class $anon extends scala.AnyRef {
          def this(): anonymous class $anon = {
            $anon.super.this();
            ()
          };
          class C extends java.lang.Object with ScalaObject {
            <synthetic> def p$default$1[T >: Null <: Any]: Null @scala.annotation.unchecked.uncheckedVariance = null;
            def this(): this.C = {
              C.super.this();
              ()
            };
            def p[T >: Null <: Any](t: T = null): T = t
          }
        };
        {
          new $anon();
          ()
        }
      }
    }
  }
于 2010-03-24T09:48:05.743 回答