2010-03-16 12 views
32

@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.¿Cuándo se necesita @uncheckedVariance en Scala, y por qué se usa en GenericTraversableTemplate?

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 los parámetros y nunca en un tipo de retorno.

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?

+0

Muy buena pregunta! –

Respuesta

25

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 es invariablemente rey).

Las comprobaciones de tipos 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 la anotación de varianza (todavía ;-)) en GenericTraversableTemplate, de modo que pudiéramos instanciarla a una de las dos dependiendo de la subclase, tenemos que recurrir a la fundición (@uncheckVariance es esencialmente una kind-cast). Para leer más, recomiendo mi tesis (lo siento ;-)) o nuestro reciente bitrot paper

+0

Gracias! Disfruté leyendo el documento de bitrot la semana pasada, pero no trata específicamente el problema de integrar colecciones co y variante bajo un padre común. Supongo que veré lo que hay en tu disertación :) – retronym

+0

Bueno, en su mayor parte fue un complemento desvergonzado: mi disertación en realidad no trata directamente ese problema exacto. Aunque debería tener algunas reflexiones más sobre ese tipo de polimorfismo más poderoso. Agregaré algunos pensamientos más abajo. –

+0

"invariablemente rey" ... ja, ja –

8

En mi tesis describo un cálculo, Scalina, que tiene límites & anotaciones de varianza como parte del lenguaje de clase (una versión anterior es también disponible como workshop paper). 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" se explica muy bien por Edsko de Vries en el contexto de los tipos de singularidad.

Uniqueness Typing Simplified, 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 sistema de singularidad tipo que es más simple que tanto Limpio sistema de exclusividad y el sistema que propusimos anteriormente. El nuevo sistema de tipo es simple para implementar y agregar a los compiladores existentes , y se puede ampliar fácilmente con características avanzadas como mayores tipos de rango e impredicativity. Nosotros describimos nuestra implementación en Morrow, , un lenguaje funcional experimental con ambas características.Finalmente, comprobamos la solidez del sistema de núcleo con respecto al cálculo lambda call-by-need.

5

encontré otra vez en que se utiliza @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(); 
     () 
     } 
     } 
    } 
    }