Warum Kompilierung-generative Techniken zur Struktur Typisierung getrennte Sammlung verhindern?
-
29-09-2019 - |
Frage
Ich las (ok, Skimming) Dubochet und Odersky compilieren Konstruktionstypen auf die JVM und wurde nach dem folgenden Anspruch verwirrt:
Generative Techniken erstellen Java-Schnittstellen stehen in für Strukturtypen auf der JVM. Die Komplexität solcher Techniken liegt darin, dass alle Klassen, die verwendet werden sollen als Strukturtypen überall im Programm implementieren muss die richtigen Schnittstellen. Wenn diese zum Zeitpunkt der Kompilierung durchgeführt, es verhindert getrennte Sammlung.
(Hervorhebung hinzugefügt)
Sie sich das Autoclose Beispiel aus dem Papier:
type Closeable = Any { def close(): Unit }
def autoclose(t: Closeable)(run: Closeable => Unit): Unit = {
try { run(t) }
finally { t.close }
}
Könnten wir nicht eine Schnittstelle für den Closeable
Typ erzeugen wie folgt:
public interface AnonymousInterface1 {
public void close();
}
und verwandelt unsere Definition von autoclose
zu
// 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 }
}
Dann einen Anruf Ort für autoclose
betrachten:
val fis = new FileInputStream(new File("f.txt"))
autoclose(fis) { ... }
Da fis
ein FileInputStream
ist, die nicht AnonymousInterface1
nicht implementieren, müssen wir einen Wrapper generieren:
class FileInputStreamAnonymousInterface1Proxy(val self: FileInputStream)
extends AnonymousInterface1 {
def close() = self.close();
}
object FileInputStreamAnonymousInterface1Proxy {
implicit def fis2proxy(fis: FileInputStream): FileInputStreamAnonymousInterface1Proxy =
new FileInputStreamAnonymousInterface1Proxy(fis)
}
Ich muss fehlen, etwas , aber es ist mir unklar, was es ist. Warum sollte dieser Ansatz verhindert getrennte Sammlung?
Lösung
Ich benutze den impliziten Ansatz (mit typeclasses) Sie beschreiben in der Scala ARM-Bibliothek . Denken Sie daran, dass dies eine Hand codierte Lösung für das Problem.
Das größte Problem hier ist implizite Auflösung. Der Compiler wird nicht Wrapper on the fly für Sie generieren, müssen Sie dies vor der Zeit tun und stellen Sie sicher, sie sind eine der implizite Umfang. Diese Mittel (für Scala-ARM), dass wir „gemeinsam“ Wrapper für was auch immer Ressourcen bieten wir können, und fallen zurück auf Reflexion basierende Typen, wenn wir nicht die entsprechenden Wrapper finden. Dies ergibt den Vorteil, dass der Benutzer zu ermöglichen, ihre eigenen Wrapper mit normalen impliziten Regeln angeben.
Siehe auch: Die Ressourcentyp-Eigenschaft und alle seine vordefinierte Wrapper.
Auch gebloggt ich über diese Technik beschreibt die implizite Auflösung Magie im Detail: Monkey Patching, Duck Typing und Typklassen .
In jedem Fall werden Sie wahrscheinlich nicht wollen, zu hand kodieren eine Typklasse jedes Mal wenn Sie Strukturtypen verwenden. Wenn Sie tatsächlich die Compiler automatisch eine Schnittstelle zu erstellen und zu tun, die Magie für Sie wollen, könnte es chaotisch. Jedesmal, wenn man einen Strukturtyp definiert, wird der Compiler eine Schnittstelle für sie schaffen (irgendwo im Äther vielleicht?). Wir müssen jetzt Namespaces für diese Dinge hinzuzufügen. Auch bei jedem Aufruf wird der Compiler muss irgendeine Art von Wrapper-Implementierungsklasse erzeugt (wieder mit der Namensraum Ausgabe). Schließlich, wenn wir zwei verschiedene Methoden mit dem gleichen Strukturtyp aufweisen, die separat kompiliert werden, haben wir explodierte nur die Zahl der Schnittstellen benötigen wir.
Nicht, dass die Hürde nicht überwunden werden kann, aber wenn Sie wollen strukturelle Typisierung haben, mit „direktem“ Zugang für bestimmte Typen des Typ-Trait-Muster scheint die beste Wahl für heute zu sein.
Andere Tipps
Wie ich mich erinnere von einer Diskussion auf dem
Denken Sie darüber nach. Betrachten Klasse A Einige Stelle im Programm, die möglicherweise in einer separat kompilierte Bibliothek, diese Strukturtyp verwendet wird: und anderswo, dieser verwendet wird: Wie, abgesehen von den globalen Analyse, ist die Klasse A mit den Schnittstellen eingerichtet werden, notwendig, es zu ermöglichen, verwendet werden, wenn diese weit entfernten Strukturtypen angegeben werden? Wenn jede mögliche strukturelle Art, dass eine bestimmte Klasse verwendet wird, entsprechen könnte eine Schnittstelle Erfassung, dass die Strukturtyp zu erzeugen, gibt es eine Explosion solcher Schnittstellen. Denken Sie daran, dass ein Strukturtyp mehr als ein erforderliches Element kann erwähnen, so dass für eine Klasse mit N öffentlichen Elementen (vals oder defs), um all möglichen Teilmengen von jenen N erforderlich sind, und das ist der Powerset von N, deren Mächtigkeit ist 2 ^ N. class A { def a1(i: Int): String = { ... }; def a2(s: String): Boolean = { ... }
{ def a1(i: Int): String }
{ def a2(s: String): Boolean }