¿Por qué las técnicas generativas en tiempo de compilación para la tipificación estructural impiden compilación separada?

StackOverflow https://stackoverflow.com/questions/3498954

  •  29-09-2019
  •  | 
  •  

Pregunta

Compilación estructurales (ok, descremado) Dubochet y de Odersky en la JVM y fue confundido por la siguiente afirmación:

técnicas generativas crean interfaces Java para estar en para los tipos estructurales en la JVM. La complejidad de tales técnicas radica en que todas las clases que han de ser utilizados como Los tipos estructurales en cualquier parte del programa debe aplicar las interfaces correctas. Cuando esto se hace en tiempo de compilación, se evita la compilación por separado.

(énfasis añadido)

Considere el ejemplo autoclose del papel:

type Closeable = Any { def close(): Unit }

def autoclose(t: Closeable)(run: Closeable => Unit): Unit = {
   try { run(t) }
   finally { t.close }
}

No podríamos generar una interfaz para el tipo Closeable de la siguiente manera:

public interface AnonymousInterface1 {
   public void close();
}

y transformar nuestra definición de autoclose a

// UPDATE: using a view bound here, so implicit conversion is applied on-demand
def autoclose[T <% AnonymousInterface1](t: T)(run: T => Unit): Unit = {
   try { run(t) }
   finally { t.close }
}

A continuación, considere una llamada in situ para autoclose:

val fis = new FileInputStream(new File("f.txt"))
autoclose(fis) { ... }

Desde fis es un FileInputStream, que no implementa AnonymousInterface1, tenemos que generar un envoltorio:

class FileInputStreamAnonymousInterface1Proxy(val self: FileInputStream) 
      extends AnonymousInterface1 {
   def close() = self.close();
}

object FileInputStreamAnonymousInterface1Proxy {
   implicit def fis2proxy(fis: FileInputStream): FileInputStreamAnonymousInterface1Proxy =
      new FileInputStreamAnonymousInterface1Proxy(fis)
}

Debo estar perdiendo algo , pero no está claro para mí lo que es. ¿Por qué este enfoque prevenir la compilación separada?

¿Fue útil?

Solución

En realidad, utilizar el enfoque implícita (mediante clases de tipos) que usted describe en el Scala ARM biblioteca . Recuerda que esta es una solución codificado a mano al problema.

El mayor problema aquí es la resolución implícita. El compilador no generar contenedores para que sobre la marcha, debe hacerlo antes de tiempo y asegurarse de que son uno el alcance implícito. Esto significa (para Scala-ARM) que nos ofrecen envoltorio "común" para todos los recursos que podamos, y caen de nuevo a tipos basados ??en la reflexión cuando no podemos encontrar el contenedor adecuado. Esto le da la ventaja de permitir al usuario especificar su propio envoltorio utilizando reglas implícitas normales.

Ver: El Tipo-rasgo de recursos y todos los envoltorios es predefinido.

Además, me escribió en su blog acerca de esta técnica que describe la magia resolución implícita en más detalle: mono parches, Duck Typing y clases de tipos .

En cualquier caso, es probable que no quieren mano a codificar cada una clase de tipo que utiliza tipos estructurales. Si realmente quería el compilador para crear automáticamente una interfaz y hacer la magia para usted, podría causar problemas. Cada vez que se define un tipo estructural, el compilador tendrá que crear una interfaz para ello (en algún lugar en el éter, tal vez?). Ahora tenemos que añadir espacios de nombres para estas cosas. Además, con cada llamada al compilador tendrá que generar algún tipo de una clase contenedora-aplicación (otra vez con el tema de espacio de nombres). Por último, si tenemos dos métodos diferentes con el mismo tipo estructural que se compilan por separado, sólo hemos explotado el número de interfaces que requerimos.

No es que el obstáculo no podía ser superada, pero si usted quiere tener la tipificación estructural con el acceso "directo" para determinados tipos del patrón de tipo rasgo parece ser la mejor opción hoy en día.

Otros consejos

Por lo que recuerdo de una discusión en la Scala-Inernals lista de correo, el problema con esto es la identidad del objeto, que se conserva por el enfoque actual con la compilación, se pierde cuando se envuelve valores.

Piense en ello. Considere la clase A

class A { def a1(i: Int): String = { ... }; def a2(s: String): Boolean = { ... }

Algunos lugar en el programa, posiblemente en una biblioteca compilado por separado, se utiliza este tipo estructural:

{ def a1(i: Int): String }

y en otros lugares, éste se utiliza:

{ def a2(s: String): Boolean }

¿Cómo, además de análisis global, es de clase A para ser decorada con las interfaces necesarias para que pueda ser utilizado donde se especifican los tipos estructurales lejanos?

Si cada posible tipo estructural que una clase dada podría ajustarse a se utiliza para generar una interfaz de captura de ese tipo estructural, hay una explosión de tales interfaces. Recuerde, que un tipo estructural puede mencionar más de un miembro requerido, así que para una clase con n elementos públicos (Vals o defs) todos los posibles subconjuntos de los N son obligatorios, y esa es la powerset de N cuya cardinalidad es 2 ^ N.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top