Почему генеративные методы структурной типизации во время компиляции предотвращают раздельную компиляцию? - PullRequest
3 голосов
/ 17 августа 2010

Я читал (хорошо, скимминг) Дубошет и Одерского Компиляция структурных типов на JVM и был смущен следующим утверждением:

Генеративные методы создают Java-интерфейсы для структурноготипы на JVM.Сложность таких методов заключается в том, что все классы, которые должны использоваться в качестве структурных типов в любом месте программы, должны реализовывать правильные интерфейсы. Когда это делается во время компиляции, это предотвращает отдельную компиляцию.

(выделение добавлено)

Рассмотрим пример автозамены из статьи:

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

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

Не могли бы мы сгенерировать интерфейс для типа Closeable следующим образом:

public interface AnonymousInterface1 {
   public void close();
}

и преобразовать наше определение autoclose в

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

Затем рассмотрим вызовсайт для autoclose:

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

Поскольку fis - это FileInputStream, который не реализует AnonymousInterface1, нам нужно сгенерировать оболочку:

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

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

Iдолжно быть пропущено что-то , но мне неясно, что это такое.Почему этот подход препятствует отдельной компиляции?

Ответы [ 3 ]

7 голосов
/ 17 августа 2010

Как я помню из обсуждения списка рассылки Scala-Inernals , проблема с этим заключается в идентичности объекта, которая сохраняется при текущем подходе к компиляции, теряется, когдаВы переносите значения.

4 голосов
/ 17 августа 2010

Подумай об этом.Рассмотрим класс A

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

В некотором месте программы, возможно, в отдельно скомпилированной библиотеке, используется этот структурный тип:

{ def a1(i: Int): String }

и в других местах используется этот:

{ def a2(s: String): Boolean }

Каким образом, помимо глобального анализа, класс А должен быть украшен интерфейсами, необходимыми для его использования в тех случаях, когда указаны эти далеко идущие структурные типы?

Если все возможные конструкцииТип, которому может соответствовать данный класс, используется для генерации интерфейса, захватывающего этот структурный тип, таких интерфейсов взрывается.Помните, что структурный тип может упомянуть более одного обязательного члена, поэтому для класса с N открытыми элементами (vals или defs) требуются все возможные подмножества этих N, и это набор степеней N, мощность которого равна 2 ^ N.

1 голос
/ 29 августа 2010

Я на самом деле использую неявный подход (используя классы типов), который вы описываете в библиотеке Scala ARM . Помните, что это ручное решение проблемы.

Самая большая проблема здесь - неявное разрешение. Компилятор не будет создавать оболочки для вас на лету, вы должны сделать это заранее и убедиться, что они являются неявной областью действия. Это означает (для Scala-ARM), что мы предоставляем «общую» оболочку для любых ресурсов, которые мы можем, и прибегаем к типам на основе отражения, когда мы не можем найти подходящую оболочку. Это дает преимущество, позволяя пользователю указывать свою собственную оболочку, используя обычные неявные правила.

См .: Характеристика типа ресурса и все его предопределенные оболочки.

Кроме того, я написал в блоге об этой технике, описывающей магию неявного разрешения более подробно: Patching Monkey, Duck Typing и Классы типов .

В любом случае, вы, вероятно, не хотите вручную кодировать класс типов каждый раз, когда используете структурные типы. Если вы действительно хотите, чтобы компилятор автоматически создал интерфейс и сделал для вас чудеса, он может запутаться. Каждый раз, когда вы определяете структурный тип, компилятор должен будет создать для него интерфейс (возможно, где-нибудь в эфире?). Теперь нам нужно добавить пространства имен для этих вещей. Кроме того, при каждом вызове компилятор должен генерировать некоторый класс реализации-оболочки (опять же с проблемой пространства имен). Наконец, если у нас есть два разных метода с одним и тем же структурным типом, которые компилируются отдельно, мы только что разбили количество необходимых нам интерфейсов.

Не то чтобы препятствие не могло быть преодолено, но если вы хотите использовать структурную типизацию с «прямым» доступом для определенных типов, шаблон «тип-черта», похоже, будет вашей лучшей ставкой сегодня.

...