Cuando se necesita @uncheckedVariance en Scala, y por qué se utiliza en GenericTraversableTemplate?
-
20-09-2019 - |
Pregunta
@uncheckedVariance
se puede utilizar para cerrar la brecha entre las anotaciones de varianza sitio de la declaración de Scala y genéricos invariantes de 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
Esto dice que java.util.Comparator es naturalmente contra-variante, que es el parámetro de tipo T
aparece en parámetros y nunca en un tipo de retorno.
Esto plantea la pregunta: ¿por qué se utilizó también en la biblioteca de colecciones Scala que no se extiende desde interfaces Java
trait GenericTraversableTemplate[+A, +CC[X] <: Traversable[X]] extends HasNewBuilder[A, CC[A] @uncheckedVariance]
¿Cuáles son los usos válidos para esta anotación?
Solución
El problema es que se utiliza GenericTraversableTemplate dos veces: una vez para colecciones mutables (donde su parámetro de tipo debe ser invariante), y una vez para colecciones inmutables (donde covarianza es invariablemente rey)
.typechecks de GenericTraversableTemplate asumir cualquiera de covarianza o invariancia para el parámetro de un tipo. Sin embargo, cuando heredamos en un rasgo mutable, tenemos que elegir invariancia. Por el contrario, nos gustaría covarianza en una subclase inmutable.
Dado que no podemos abstracta sobre la anotación varianza (todavía ;-)) en GenericTraversableTemplate, por lo que podríamos haber una instancia que a cualquiera de ellos dependiendo de la subclase, tenemos que recurrir a la fundición (@uncheckVariance es esencialmente una especie -emitir). Para leer más, recomiendo mi tesis (lo siento ;-)) o nuestro reciente papel bitrot
Otros consejos
En mi tesis describo un cálculo, Scalina, que tiene límites y anotaciones de varianza como parte del lenguaje de clase (una versión anterior también está disponible como un taller papel ). La relevancia de esta discusión es el siguiente paso que quiero tomar en el desarrollo de este cálculo: construir otra capa por encima de eso, para que pueda abstracta sobre los límites (Fácil) y anotaciones de varianza (hace girar mi cabeza). En realidad, usted no sólo tachuela 1 capa extra de allí, sino generalizar sus construcciones polimorfismo por lo que trabajar en todos los niveles, y hacer sus "atributos" (cotas, anotaciones de varianza, argumentos implícitos necesarios, ...) en tipos regulares con tipos especiales, que son todos sujetos a la abstracción.
Los atributos son "tipos" idea se explica muy bien por Edsko de Vries en el contexto de tipos de singularidad.
Singularidad Typing simplificado , Edsko de Vries, Rinus Plasmeijer, y David Abrahamson. En Olaf Chitil, Zoltán Horváth y Viktória Zsók (Eds.): IFL 2007, LNCS 5083, pp. 201-218, 2008.
Resumen: Se presenta un tipo de singularidad sistema que es más simple que tanto sistema de la singularidad de limpia y el sistema que propusimos anteriormente. El nuevo Tipo de sistema es sencillo implementar y añadir a la ya existente compiladores, y fácilmente se pueden ampliar con características avanzadas tales como mayor tipos de rango y impredicativity. Nosotros describir nuestra aplicación en Morrow, un lenguaje funcional experimental con estas dos características. Finalmente nosotros demostrar solidez del tipo de núcleo sistema con respecto a la Call-by-necesidad cálculo lambda.
he encontrado otra vez, cuando se utilice @uncheckedVariance - el método sintético que devuelve el valor predeterminado para un parámetro de un tipo abstracto:
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();
()
}
}
}
}