parameter - ¿Cuándo se necesita @uncheckedVariance en Scala, y por qué se usa en GenericTraversableTemplate?
scala type (3)
@uncheckedVariance
se puede utilizar para cerrar la brecha entre las anotaciones de varianza del sitio de declaración de Scala y los genéricos invariables 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 contravariante, que es el parámetro de tipo T
aparece en los parámetros y nunca en un tipo de devolución.
Esto plantea la pregunta: ¿por qué también se utiliza en la biblioteca de colecciones de Scala que no se extiende desde las interfaces de 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?
El problema es que GenericTraversableTemplate se usa dos veces: una para colecciones mutables (donde su parámetro de tipo debe ser invariante) y una para colecciones inmutables (donde la covarianza invariablemente es el rey).
Las comprobaciones de tipo de GenericTraversableTemplate suponen covarianza o invarianza para el parámetro tipo A. Sin embargo, cuando lo heredamos en un rasgo mutable, tenemos que elegir la invarianza. Por el contrario, nos gustaría la covarianza en una subclase inmutable.
Dado que no podemos abstraer sobre la anotación de varianza (todavía ;-)) en GenericTraversableTemplate, de modo que pudiéramos instanciarlo a cualquiera de los dos dependiendo de la subclase, tenemos que recurrir a la conversión (@uncheckVariance es esencialmente un elenco de clase) . Para leer más, recomiendo mi disertación (lo siento ;-)) o nuestro reciente documento de bitrot
En mi tesis describo un cálculo, Scalina, que tiene anotaciones de límites y varianza como parte del lenguaje amable (una versión anterior también está disponible como un trabajo de taller ). La relevancia de esta discusión es el próximo paso que quiero dar para desarrollar este cálculo: construir otra capa además de eso para que pueda abstraer sobre límites (fácil) y anotaciones de varianza (me da vueltas la cabeza). En realidad, no solo insertarás 1 capa extra, sino que generalizarás tus construcciones de polimorfismo para que funcionen en todos los niveles y conviertas tus "atributos" (límites, anotaciones de varianza, argumentos implícitos requeridos, ...) en tipos regulares con tipos especiales, que están todos sujetos a abstracción.
La idea de "los atributos son tipos" es explicada muy bien por Edsko de Vries en el contexto de los tipos de singularidad.
Singularidad Simplificada , 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: Presentamos un sistema de tipo de singularidad que es más simple que el sistema de exclusividad de Clean y el sistema que propusimos anteriormente. El nuevo sistema de tipo es sencillo de implementar y agregar a los compiladores existentes, y se puede ampliar fácilmente con funciones avanzadas como tipos de rango más altos e impredicatividad. Describimos nuestra implementación en Morrow, un lenguaje funcional experimental con estas dos características. Finalmente, demostramos la solidez del sistema de tipo núcleo con respecto al cálculo de lambda llamada por necesidad.
Encontré otra vez donde se usa @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();
()
}
}
}
}