Proxies/delegados en Scala
delegates lisp (2)
Recientemente he visto varias preguntas de Scala (por ejemplo, here , here y here ) que pedían el uso de proxies, y surgieron más de una vez en mi propio trabajo. La biblioteca de Scala tiene una serie de características de proxy (14, si conté correctamente).
Las clases / rasgos de proxy usualmente contienen un montón 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 de Proxy[T]
que podría usarse de la siguiente manera:
class FooProxy(val self: Foo) extends Proxy[Foo] {
// added behavior
def mymethod = ...
}
donde el trait Proxy[T] extends T
Por supuesto, en realidad no es posible definir el rasgo de Proxy
sin la magia del compilador.
Mi siguiente pensamiento fue buscar un complemento del compilador (tal 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é el complemento AutoProxy de Kevin Wright . El complemento está diseñado para resolver el problema del proxy de forma ordenada, junto con otros casos de uso (incluidos los mixins dinámicos):
class FooProxy(@proxy val self: Foo) { ... }
Desafortunadamente, parece que el trabajo en él se estancó en noviembre (2009). Entonces, mis preguntas son
- ¿Hay trabajo continuo en el complemento AutoProxy?
- ¿Esto encontrará su camino en el compilador?
- ¿Se están considerando otros enfoques?
- Finalmente, ¿esto apunta a una debilidad significativa en Scala? Después de todo, ¿no sería posible definir un rasgo de
Proxy
dado macros de estilo lisp?
Adam Warski recientemente hizo un blog sobre un enfoque basado en Macro que puede funcionar en Scala 2.11 y definitivamente funciona con el complemento del compilador Macro Paradise en Scala 2.10.
Esta biblioteca te 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 etapa muy temprana de prueba de concepto en el momento de redactar este documento, por lo que se recomienda precaución.
Cuatro preguntas, cuatro respuestas.
¡Yo soy, aunque la familia tiene que venir primero! Además, otros están involucrados en analizar el problema general con los métodos de síntesis en un complemento del compilador.
Si es así, lo más probable es que esté en una forma diferente, quizás sin usar anotaciones.
No conozco ningún complemento equivalente, aunque uno de los proyectos candidatos a 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 ningún complemento de compilador: define una conversión implícita de FooProxy a Foo que simplemente devuelve el miembro
self
; esto te llevará la mayor parte del camino 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 que debe tener en cuenta.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 debería haber una forma elegante de manejar la tarea.