Почему генеративные методы компиляции для структурного типирования предотвращают отдельную компиляцию?

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

  •  29-09-2019
  •  | 
  •  

Вопрос

Я читал (ОК, Скимин) Дюбошет и Одерский Компиляция конструкционных типов на 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)
}

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

Это было полезно?

Решение

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

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

Видеть: Тип ресурса И все это предопределенные обертки.

Кроме того, я написал в блоге об этой технике, описывающей магию неявного разрешения более подробно: Платформа обезьяны, утка, набрав и классы типа.

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

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

Другие советы

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

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

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

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

{ def a1(i: Int): String }

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

{ def a2(s: String): Boolean }

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top