tutorial programacion parte funcional español curso java generics scala jvm

java - parte - programacion funcional



¿Es posible que Scala haya verificado los genéricos sin cambiar la JVM? (5)

Recientemente comencé a aprender Scala y me decepcionó (pero no me sorprendió) que sus genéricos también se implementen a través del borrado de tipos.

Mi pregunta es: ¿es posible que Scala haya reificado los genéricos o sería necesario cambiar la JVM de alguna manera? Si es necesario cambiar la JVM, ¿qué es exactamente lo que se debe cambiar?


"Manifiesto implícito" es un truco de compilación de Scala y no hace que los genéricos en Scala sean reificados. El compilador Scala, cuando ve una función con el parámetro "implícito m: Manifest [A]" y conoce el tipo genérico de A en el sitio de la llamada, envolverá la clase de A y sus parámetros de tipo genérico en un Manifest y hará Está disponible dentro de la función. Sin embargo, si no pudo descubrir el verdadero tipo de A, entonces no tiene forma de crear un Manifiesto. En otras palabras, Manifest tiene que pasar a lo largo de la cadena de llamada de funciones si la función interna lo necesita.

scala> def typeName[A](a: A)(implicit m: reflect.Manifest[A]) = m.toString typeName: [A](a: A)(implicit m: scala.reflect.Manifest[A])java.lang.String scala> typeName(List(1)) res6: java.lang.String = scala.collection.immutable.List[int] scala> def foo[A](a: A) = typeName(a) <console>:5: error: could not find implicit value for parameter m:scala.reflect.Manifest[A]. def foo[A](a: A) = typeName(a) ^ scala> def foo[A](a: A)(implicit m: reflect.Manifest[A]) = typeName(a) foo: [A](a: A)(implicit m: scala.reflect.Manifest[A])java.lang.String scala> foo(Set("hello")) res8: java.lang.String = scala.collection.immutable.Set[java.lang.String]


No: Scala no puede ejecutarse como un bytecode equivalente a Java si ese bytecode no admite los genéricos reificados.

Cuando preguntas "¿qué es lo que necesita ser cambiado?" , la respuesta es: la especificación del bytecode . Actualmente, el bytecode no permite que se defina el tipo parametrizado de una variable. Se ha decidido que, como una modificación del código de bytes para admitir los genéricos reificados, rompería la compatibilidad hacia atrás , los genéricos deberían implementarse a través del borrado de tipos .

Para solucionar esto, Scala ha utilizado el poder de su mecanismo implicit para definir un Manifest que se puede importar en cualquier ámbito para descubrir información de tipo en tiempo de ejecución. Los manifiestos son experimentales y en gran parte indocumentados, pero vienen como parte de la biblioteca en 2.8 . Aquí hay otro buen recurso sobre los genéricos / Manifiestos reificados de Scala.


Para complementar la respuesta de oxbow_lakes: No es posible y parece que nunca sucederá (al menos pronto).

Las razones (refutables) que JVM no admite los genéricos reificados parecen ser:

  • Menor rendimiento.
  • Se rompe la compatibilidad hacia atrás. Se puede resolver duplicando y arreglando muchas bibliotecas.
  • Se puede implementar utilizando manifiestos: la "solución" y el mayor impedimento.

Referencias:

Puede compararlo fácilmente y ver que el impacto en el rendimiento es muy notable. Especialmente el consumo de memoria aumenta mucho.

Creo que el camino a seguir es tener una reificación opcional como empezamos a hacer en Scala con Manifests / TypeTags.

Si puede y lo combina con la especialización en tiempo de ejecución, puede aspirar a obtener un alto rendimiento y un código genérico. Sin embargo, ese es probablemente el objetivo para Scala 2.12 o 2.13.



Una vez que Scalac es un compilador, tiene el potencial de poder embellecer el código generado con las estructuras de datos necesarias para implementar genéricos reificados.

Lo que quiero decir es que Scalac tendría la capacidad de ver ...

// definition class Klass[T] { value : T } //calls floats = Klass[float] doubles = Klass[double]

... y "expandir" a algo como esto:

// definition class Klass_float { value : float } class Klass_double { value : double } // calls floats = Klass_float doubles = Klass_double

Editar

El punto es: el compilador tiene la capacidad de crear todas las estructuras de datos necesarias que demuestren ser necesarias para proporcionar información de tipo adicional en tiempo de ejecución. Una vez que esta información de tipo esté disponible, el tiempo de ejecución de Scala lo aprovecharía y podría realizar todas las operaciones de reconocimiento de tipo que podamos imaginar. No importa si la JVM proporciona un código de bytes para los genéricos reificados o no. El trabajo no lo realiza la JVM, sino la biblioteca de Scala.

Si ya ha escrito un depurador simbólico (¡lo hice!), Sabe que básicamente puede "volcar" toda la información que el compilador tiene en tiempo de compilación en el binario generado, adoptando cualquier organización de datos que demuestre que es más conveniente para su posterior procesamiento. Esta es exactamente la misma idea: ''volcar'' toda la información de tipo que tiene el compilador de Scala.

En pocas palabras, no veo por qué no podría ser posible, no importa si la JVM proporciona operaciones nativas para genéricos reificados o no. El código de bytes JVM no tiene nada que ver con los genéricos reificados. Este tipo de cosas es una cuestión de especificación de idioma, características del compilador y soporte de biblioteca en tiempo de ejecución.

Otra edición

IBM X10 demuestra la capacidad de la que estoy hablando: compila código X10 en código Java, aprovechando genéricos reificados en plataformas Java. Como mencioné antes: se puede hacer (¡y lo hizo IBM X10!) Pero este tipo de característica involucra la especificación del idioma, el soporte del compilador (o los complementos del compilador) y suficiente soporte en las bibliotecas de tiempo de ejecución. Más información en: x10.sourceforge.net/documentation/papers/X10Workshop2012/slides/…