Когда @uncheckedVariance необходимо в Scala и почему он используется в GenericTraversableTemplate?
@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
появляется в параметрах, а не в возвращаемом типе.
Возникает вопрос: почему он также используется в библиотеке коллекций Scala, которая не распространяется на интерфейсы Java?
trait GenericTraversableTemplate[+A, +CC[X] <: Traversable[X]] extends HasNewBuilder[A, CC[A] @uncheckedVariance]
Как правильно использовать эту аннотацию?
Ответы
Ответ 1
Проблема заключается в том, что GenericTraversableTemplate используется дважды: один раз для изменяемых коллекций (где его параметр типа должен быть инвариантным) и один раз для неизменяемых коллекций (где ковариация неизменно царит).
GenericTraversableTemplate typechecks, предполагающий либо ковариацию, либо инвариантность для параметра типа A. Однако, когда мы наследуем его в изменчивом признаке, мы должны выбрать инвариантность. И наоборот, мы хотели бы ковариацию в неизменяемом подклассе.
Так как мы не можем абстрагироваться от аннотации дисперсии (пока;-)) в GenericTraversableTemplate, так что мы могли бы создать экземпляр одного из них в зависимости от подкласса, мы должны прибегнуть к кастингу (@uncheckVariance - это по существу вид -бросать). Для дальнейшего чтения я рекомендую свою диссертацию (извините;-)) или нашу недавнюю bitrot paper
Ответ 2
В моем тезисе я описываю исчисление Scalina, которое имеет аннотации границ и вариаций как часть родственного языка (более ранняя версия также доступна в качестве семинара бумага). Актуальность этого обсуждения - это следующий шаг, который я хочу предпринять для разработки этого исчисления: постройте еще один слой поверх этого, чтобы вы могли абстрагироваться от аннотаций с ограничениями (просто) и вариации (заставляет мою голову вращаться). На самом деле, вы бы просто не наложили на него 1 дополнительный слой, а скорее обобщили свои конструкции полиморфизма, чтобы они работали на всех уровнях и делали ваши "атрибуты" (границы, аннотации вариаций, требуемые неявные аргументы...) в обычные типы со специальными видами, которые все подлежат абстракции.
Идея "атрибуты типа" хорошо объясняется Эдско де Врисом в контексте уникальных типов.
Уникальность впечатывания упрощена, Эдско де Фриза, Ринуса Пласмайера и Дэвида Абрахамсона. В Олафе Читиле, Золтан Хорват и Виктория Зсок (Ред.): IFL 2007, LNCS 5083, pp. 201-218, 2008.
Аннотация: Приведем тип единственности система, которая проще, чем обе Очистить систему уникальности и которую мы предложили ранее. Новый система типа проста внедрять и дополнять существующие компиляторов, и их можно легко расширить с расширенными функциями, такими как более высокие ранговых типов и непроизводительности. Мы описать нашу реализацию в Морроу, экспериментальный функциональный язык обе эти функции. Наконец, мы доказать надежность основного типа системы в отношении по требованию лямбда-исчисления.
Ответ 3
Я нашел еще раз, когда используется @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();
()
}
}
}
}