2010-08-13 26 views
16

He visto varias preguntas de Scala recientemente (por ejemplo, here, here y here) que requieren el uso de proxies, y ha aparecido más de una vez en mi propio trabajo. La biblioteca de Scala tiene varios rasgos de proxy (14, si conté correctamente). clasesProxies/delegados en Scala

Proxy/rasgos generalmente contienen gran cantidad de repetitivo:

class FooProxy(val self: Foo) extends Foo { 
    // added behavior 
    def mymethod = ... 

    // forwarding methods 
    def method1 = self.method1 
    def method2(arg: String) = self.method2(arg) 
    ... 
} 

trait Foo { 
    def method1: Unit 
    def method2(arg: String): Unit 
} 

Mi primer pensamiento fue definir un rasgo Proxy[T] que podría ser utilizado de la siguiente manera:

class FooProxy(val self: Foo) extends Proxy[Foo] { 
    // added behavior 
    def mymethod = ... 
} 

donde trait Proxy[T] extends T. Por supuesto, no es posible definir el rasgo Proxy sin la magia del compilador.

Mi siguiente pensamiento fue buscar un complemento de compilación (esta capacidad claramente no está en el compilador existente, o las fuentes para esos 14 rasgos de proxy serían mucho más pequeñas). Efectivamente, encontré Kevin Wright's AutoProxy plugin. El plugin está diseñado para resolver el problema de proxy cuidadosamente, junto con otros casos de uso (incluyendo mixins dinámicos):

class FooProxy(@proxy val self: Foo) { ... } 

Por desgracia, parece que trabajar en él se estancó en noviembre (2009). Entonces, mis preguntas son

  1. ¿Continúa el trabajo en el complemento AutoProxy?
  2. ¿Funcionará esto en el compilador?
  3. ¿Se están considerando otros enfoques?
  4. Finalmente, ¿apunta esto a una debilidad significativa en Scala? Después de todo, ¿no sería posible definir un rasgo Proxy dado macros de estilo lisp?
+0

Los rasgos no pueden tener parámetros. ¿Estás proponiendo que se agreguen? Además, no ha mostrado nada que no pueda solucionarse con la adición de una conversión implícita. ¿La propuesta de crear una conversión implícita es innecesaria? –

+0

"Los rasgos no pueden tener parámetros": error tonto, reparado. –

+0

Las conversiones implícitas resuelven problemas similares, pero no siempre son adecuadas (de lo contrario, ¿por qué los tipos de EPFL incluirían tantos proxies en la biblioteca de Scala?). Por un lado, incurren en más gastos generales que la delegación. En segundo lugar, el uso extensivo de la conversión implícita puede dañar la capacidad de mantenimiento/legibilidad. –

Respuesta

6

Cuatro preguntas, cuatro respuestas

  1. estoy, aunque la familia es lo primero! Además, otros están involucrados en analizar el problema general de la sintetización de métodos en un plugin de compilación.

  2. Si es así, lo más probable es que esté en una forma diferente, quizás sin utilizar anotaciones.

  3. No conozco ningún complemento equivalente, aunque uno de los proyectos de candidatos de Scala GSOC se basó en parte en mi código de autoproxy. Sin embargo, hay una solución muy limpia que funcionará en la mayoría de los casos y no necesita un plugin de compilación en absoluto: usted define una conversión implícita de FooProxy a Foo que simplemente devuelve el miembro self; esto te llevará la mayor parte del camino hasta allí. Los principales problemas con el enfoque son que hará la vida más difícil si necesita usar su código de Java, puede ser menos eficiente en términos de velocidad/memoria, y es otra cosa implícita que debe tener en cuenta.

  4. La parte frustrante es que casi toda la lógica necesaria ya está disponible en el compilador, y se usa para mixins, por lo que realmente debe ser una manera elegante de manejar la tarea.

+0

Comprueba la transformación @Delegate AST en Groovy. Es realmente bueno. http://docs.codehaus.org/display/GROOVY/Delegate+transformation – sourcedelica

5

Adam Warski recently blogged sobre a Macro-based approach que pueden trabajar en Scala 2.11 y definitivamente funciona con el plug-in Paradise compilador macro en Scala 2.10.

Esta biblioteca le permitiría escribir

class FooProxy(@delegate wrapped: Foo) extends Foo { 
    // added behavior 
    def mymethod = ... 

    // forwarding methods (generated for you) 
    // def method1 = wrapped.method1 
    // def method2(arg: String) = wrapped.method2(arg) 
} 

El proyecto se encuentra en una muy temprana, la prueba de concepto, el escenario en el momento de escribir estas líneas, por lo que se recomienda precaución.