I was reading (ok, skimming) Dubochet and Odersky's Compiling Structural Types on the JVM and was confused by the following claim:
Generative techniques create Java interfaces to stand in for structural types on the JVM. The complexity of such techniques lies in that all classes that are to be used as structural types anywhere in the program must implement the right interfaces. When this is done at compile time, it prevents separate compilation.
(emphasis added)
Consider the autoclose example from the paper:
type Closeable = Any { def close(): Unit }
def autoclose(t: Closeable)(run: Closeable => Unit): Unit = {
try { run(t) }
finally { t.close }
}
Couldn't we generate an interface for the Closeable
type as follows:
public interface AnonymousInterface1 {
public void close();
}
and transform our definition of autoclose
to
// 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 }
}
Then consider a call-site for autoclose
:
val fis = new FileInputStream(new File("f.txt"))
autoclose(fis) { ... }
Since fis
is a FileInputStream
, which does not implement AnonymousInterface1
, we need to generate a wrapper:
class FileInputStreamAnonymousInterface1Proxy(val self: FileInputStream)
extends AnonymousInterface1 {
def close() = self.close();
}
object FileInputStreamAnonymousInterface1Proxy {
implicit def fis2proxy(fis: FileInputStream): FileInputStreamAnonymousInterface1Proxy =
new FileInputStreamAnonymousInterface1Proxy(fis)
}
I must be missing something, but it's unclear to me what it is. Why would this approach prevent separate compilation?