Прокси / делегаты в Scala

Недавно я видел несколько вопросов по Scala (например, здесь, здесь и здесь), в котором требовалось использовать прокси, и это неоднократно возникало в моей собственной работе. В библиотеке Scala есть несколько прокси-черт (14, если я правильно посчитал).

Прокси-классы / трейты обычно содержат множество шаблонов:

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
}

Моей первой мыслью было определить черту Proxy[T], которую можно было бы использовать следующим образом:

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

где trait Proxy[T] extends T. Конечно, на самом деле невозможно определить черту Proxy без магии компилятора.

Следующей моей мыслью было поискать плагин компилятора (такой возможности явно нет в существующем компиляторе, иначе исходники для этих 14 свойств прокси были бы намного меньше). Конечно же, я нашел плагин AutoProxy Кевина Райта. Плагин предназначен для аккуратного решения проблемы прокси, наряду с другими вариантами использования (включая динамические миксины):

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

К сожалению, похоже, что работа над ним застопорилась в ноябре (2009 г.). Итак, мои вопросы

  1. Продолжается ли работа над плагином AutoProxy?
  2. Попадёт ли это в компилятор?
  3. Рассматриваются ли какие-либо другие подходы?
  4. Наконец, указывает ли это на существенную слабость Scala? В конце концов, разве нельзя было бы определить Proxy трейт с помощью макросов в стиле lisp?

person Aaron Novstrup    schedule 13.08.2010    source источник
comment
У черт не может быть параметров. Вы предлагаете их добавить? Кроме того, вы не показали ничего, что нельзя исправить добавлением неявного преобразования. Является ли предложение о том, что создание неявного преобразования является ненужным шаблоном?   -  person Rex Kerr    schedule 13.08.2010
comment
У черт не может быть параметров: глупая ошибка, исправлено.   -  person Aaron Novstrup    schedule 13.08.2010
comment
Неявные преобразования решают аналогичные проблемы, но они не всегда подходят (иначе зачем ребятам из EPFL включать так много прокси в библиотеку Scala?). Во-первых, они влекут за собой больше накладных расходов, чем делегирование. Во-вторых, широкое использование неявного преобразования может ухудшить ремонтопригодность / удобочитаемость.   -  person Aaron Novstrup    schedule 14.08.2010
comment
Наконец, и это, вероятно, убийца, если кто-то не придумал хитроумного обходного пути, неявные преобразования не сохраняют измененное поведение. То есть, если прокси переопределяет метод, переопределение теряется при преобразовании. Так что, если я брошу свой FooProxy в List[Foo], его крутые украшения внезапно исчезнут.   -  person Aaron Novstrup    schedule 14.08.2010


Ответы (3)


Четыре вопроса, четыре ответа

  1. Да, но семья на первом месте! Кроме того, другие участвуют в рассмотрении общей проблемы синтеза методов в подключаемом модуле компилятора.

  2. Если да, то, скорее всего, он будет в другой форме, возможно, без аннотаций.

  3. Я не знаю никаких эквивалентных плагинов, хотя один из проектов-кандидатов Scala GSOC был частично основан на моем коде автопрокси. Однако есть одно очень чистое решение, которое будет работать в большинстве случаев и совсем не требует подключаемого модуля компилятора: вы определяете неявное преобразование из FooProxy в Foo, которое просто возвращает член self; это поможет вам в большинстве случаев. Основные проблемы с этим подходом заключаются в том, что он усложнит жизнь, если вам нужно использовать свой код из Java, он может быть менее эффективным с точки зрения скорости / памяти, и это еще одно неявное, о чем вы должны знать.

  4. Раздражает то, что почти вся необходимая логика уже доступна в компиляторе и используется для миксинов, так что действительно должен быть элегантный способ решения задачи.

person Kevin Wright    schedule 18.08.2010
comment
Ознакомьтесь с преобразованием @Delegate AST в Groovy. Это действительно приятно. docs.codehaus.org/display/GROOVY/Delegate+transformation - person sourcedelica; 29.04.2011

Адам Варски недавно писал в блоге об подходе на основе макросов, который может работать в Scala 2.11 и определенно работает с Плагин компилятора Macro Paradise в Scala 2.10.

Эта библиотека позволит вам написать

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)
}

На момент написания этой статьи проект находится на очень ранней стадии проверки концепции, поэтому следует соблюдать осторожность.

person Aaron Novstrup    schedule 20.09.2013

На будущее: библиотека делегат-макрос позволяет вам это делать.
Он скомпилирован с использованием 2.11, 2.12 и 2.13. Для 2.11 и 2.12 вы должны использовать плагин компиляции macro paradise, чтобы он заработал. Для 2.13 вместо этого нужно использовать флаг -Ymacro-annotations.

Используйте это так:

trait Connection {
  def method1(a: String): String
  def method2(a: String): String
  // 96 other abstract methods
  def method100(a: String): String
}

@Delegate
class MyConnection(delegatee: Connection) extends Connection {
  def method10(a: String): String = "Only method I want to implement manually"
}

// The source code above would be equivalent, after the macro expansion, to the code below
class MyConnection(delegatee: Connection) extends Connection {
  def method1(a: String): String = delegatee.method1(a)
  def method2(a: String): String = delegatee.method2(a)
  def method10(a: String): String = "Only method I need to implement manually"
  // 96 other methods that are proxied to the dependency delegatee
  def method100(a: String): String = delegatee.method100(a)
}

Он должен работать в большинстве ситуаций, в том числе когда задействованы параметры типа и несколько списков аргументов.

Отказ от ответственности: я создатель макроса.

person cmhteixeira    schedule 09.06.2021