Когда @UncheckedVariance необходимо в Scala, и почему она используется в GenericTraversableTemplate?
-
20-09-2019 - |
Вопрос
@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]
Каковы действительные использования для этой аннотации?
Решение
Проблема заключается в том, что GenericTraversableTemplate используется дважды: один раз для изменяющихся коллекций (где его параметр типа должен быть инвариантным), и один раз для неизменных коллекций (где ковариация неизменно является королем).
Generictraversabletetemplate typechecks, предполагающие либо ковариацию, либо инвариантность для параметра типа A. Однако, когда мы наследуем это в изменчивой черте, мы должны выбрать инвариантность. И наоборот, мы хотели бы ковариации в неизменном подклассе.
Поскольку мы не можем абстрагировать аннотацию дисперсии (пока ;-)) в generictraversabletemplate, чтобы мы могли создать его для любого человека в зависимости от подкласса, мы должны прибегнуть к кастингу (@uncheckvariance-это, по сути, добрый) Анкет Для дальнейшего чтения я рекомендую свою диссертацию (извините ;-)) или наш недавний биттрот бумага
Другие советы
В своем тезисе я описываю исчисление, Scalina, которое имеет границы и аннотации дисперсии как часть вида (более ранняя версия также доступна как мастерская бумага) Актуальность к этому обсуждению является следующим шагом, который я хочу сделать при разработке этого исчисления: построить еще один слой, чтобы вы могли абстрагировать границы (легкие) и аннотации дисперсии (заставляет мою голову вращаться). На самом деле, вы бы не просто прикрепили бы 1 дополнительный слой, а скорее обобщение своих полиморфизма, чтобы они работали на всех уровнях и превращали ваши «атрибуты» (границы, аннотации дисперсии, требуемые неявные аргументы, ...) в обычные типы с особыми видами, которые подчиняются абстракции.
Идея «атрибуты - это типы», которую хорошо объясняется Эдско -де -Врисом в контексте типов уникальности.
Уникальность ввода, упрощенная, Эдско де Врис, Ринус Пласмийер и Дэвид Абрахамсон. В Olaf Chitil, Zoltán Horváth и Viktória Zsók (Eds.): IFL 2007, LNCS 5083, с. 201-218, 2008.
Аннотация: Мы представляем систему типа уникальности, которая проще, чем система уникальности Clean и предлагаемой нами системы. Новая система типа проста для реализации и добавления к существующим компиляторам, и ее можно легко расширить с помощью расширенных функций, таких как типы более высоких рангов и впечатляют. Мы описываем нашу реализацию в Morrow, экспериментальный функциональный язык с обеими этими функциями. Наконец, мы доказываем обоснованность системы основного типа по отношению к исчислению Lambda-вызовов.
Я нашел в другой раз, когда используется @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();
()
}
}
}
}