为什么结构类型的编译时生成技术会阻止单独编译?

发布于 2024-09-14 07:11:50 字数 1619 浏览 2 评论 0原文

我正在阅读(好吧,略读)Dubochet 和 Odersky 的 在 JVM 上编译结构类型< /a> 并对以下声明感到困惑:

生成技术创建 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< /code>,我们需要生成一个包装器:

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

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

我一定缺少一些东西,但我不清楚它是什么。为什么这种方法会阻止单独编译?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

2024-09-21 07:11:50

我记得关于Scala-Inernals 邮件列表,问题在于对象标识,它由当前的编译方法保留,当您包装值时会丢失。

As I recall from a discussion on the Scala-Inernals mailing list, the problem with this is object identity, which is preserved by the current approach to compiling, is lost when you wrap values.

浅忆 2024-09-21 07:11:50

想一想。考虑类 A

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

在程序中的某个地方,可能在单独编译的库中,使用了这种结构类型:

{ def a1(i: Int): String }

在其他地方,使用了这种结构类型:

{ def a2(s: String): Boolean }

除了全局分析之外,类 A 是如何用允许它的必要接口进行修饰的在指定那些广泛的结构类型的地方使用?

如果给定类可能符合的每种可能的结构类型都用于生成捕获该结构类型的接口,那么此类接口就会激增。请记住,结构类型可能会提及多个必需成员,因此对于具有 N 个公共元素(val 或 defs)的类,需要这些 N 的所有可能子集,这就是基数为 2^N 的 N 的幂集。

Think about it. Consider class A

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

Some place in the program, possibly in a separately compiled library, this structural type is used:

{ def a1(i: Int): String }

and elsewhere, this one is used:

{ def a2(s: String): Boolean }

How, apart from global analysis, is class A to be decorated with the interfaces necessary to allow it to be used where those far-flung structural types are specified?

If every possible structural type that a given class could conform to is used to generate an interface capturing that structural type, there's an explosion of such interfaces. Remember, that a structural type may mention more than one required member, so for a class with N public elements (vals or defs) all the possible subsets of those N are required, and that's the powerset of N whose cardinality is 2^N.

分开我的手 2024-09-21 07:11:50

我实际上使用您在 Scala ARM 库 中描述的隐式方法(使用类型类)。请记住,这是问题的手工编码解决方案。

这里最大的问题是隐式解析。编译器不会即时为您生成包装器,您必须提前这样做并确保它们是隐式作用域之一。这意味着(对于 Scala-ARM)我们为任何资源提供“通用”包装器,并在找不到合适的包装器时回退到基于反射的类型。这提供了允许用户使用普通隐式规则指定他们自己的包装器的优点。

请参阅:资源类型- Trait 以及所有预定义的包装器。

另外,我在博客中详细介绍了这种技术,更详细地描述了隐式解析魔法:猴子修补、鸭子类型和类型类

无论如何,您可能不想每次使用结构类型时都手动编码类型类。如果您确实希望编译器自动创建一个接口并为您施展魔法,那么它可能会变得混乱。每次定义结构类型时,编译器都必须为其创建一个接口(也许在以太坊的某个地方?)。我们现在需要为这些东西添加名称空间。此外,每次调用时,编译器都必须生成某种包装实现类(同样存在命名空间问题)。最后,如果我们有两个具有相同结构类型的不同方法单独编译,那么我们就增加了所需接口的数量。

并不是说这个障碍无法克服,但如果您想要对特定类型进行“直接”访问的结构类型,那么类型特征模式似乎是您今天最好的选择。

I actually use the implicit approach (using typeclasses) you describe in the Scala ARM library. Remember that this is a hand-coded solution to the problem.

The biggest issue here is implicit resolution. The compiler will not generate wrappers for you on the fly, you must do so ahead of time and make sure they are one the implicit scope. This means (for Scala-ARM) that we provide "common" wrapper for whatever resources we can, and fall back to reflection-based types when we can't find the appropriate wrapper. This gives the advantage of allowing the user to specify their own wrapper using normal implicit rules.

See: The Resource Type-trait and all of it's predefined wrappers.

Also, I blogged about this technique describing the implicit resolution magic in more detail: Monkey Patching, Duck Typing and Type Classes.

In any case, you probably don't want to hand-encode a type class everytime you use structural types. If you actually wanted the compiler to automatically create an interface and do the magic for you, it could get messy. Everytime you define a structural type, the compiler will have to create an interface for it (somewhere in the ether perhaps?). We now need to add namespaces for these things. Also, with every call the compiler will have to generate some kind of a wrapper-implementation class (again with the namespace issue). Finally, if we have two different methods with the same structural type that are compiled separately, we've just exploded the number of interfaces we require.

Not that the hurdle couldn't be overcome, but if you want to have structural typing with "direct" access for particular types the type-trait pattern seems to be your best bet today.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文