如何在没有JVM支持的情况下在JVM语言中实现协同程序?

在阅读了Loom proposal之后提出了这个问题,它描述了在Java编程语言中实现协同程序的方法.

特别是此提案表示,要在该语言中实现此功能,将需要额外的JVM支持.

据我所知,JVM上已经有多种语言可以将协同程序作为其功能集的一部分,例如Kotlin和Scala.

那么如何在没有额外支持的情况下实现此功能,如果没有它可以有效实施?

解决方法:

tl; dr摘要

Particularly this proposal says that to implement this feature in the language the additional JVM support will be required.

当他们说“必需”时,他们的意思是“为了以一种在语言之间兼容和可互操作的方式实施”.

So how this feature is implemented without additional support

有许多方法,最容易理解它是如何工作的(但不一定最容易实现)是在JVM之上用你自己的语义实现自己的VM. (注意,这不是它实际完成的方式,这只是为什么可以完成它的直觉.)

and can it be implemented efficiently without it ?

并不是的.

稍微长一点的解释:

请注意,Project Loom的一个目标是将此抽象纯粹作为库引入.这有三个好处:

>引入新库比更改Java编程语言要容易得多.
> JVM可以使用每种语言编写的程序立即使用库,而Java程序只能使用Java语言功能.
>可以实现具有相同API但不使用新JVM功能的库,这将允许您通过简单的重新编译来编写在较旧JVM上运行的代码(尽管性能较低).

但是,将其实现为库可以排除聪明的编译器技巧,将协同例程转换为其他内容,因为不涉及编译器.如果没有聪明的编译器技巧,获得良好的性能将更加困难,因为它是JVM支持的“要求”.

更长的解释:

通常,所有通常的“强大”控制结构在计算意义上是等同的并且可以彼此使用.

最着名的那些“强大的”通用控制流结构是古老的GOTO,另一个是Continuations.然后,有线程和协同程序,以及人们通常不会想到的,但这也等同于GOTO:Exceptions.

一种不同的可能性是重新调用调用堆栈,因此调用堆栈可以作为程序员的对象访问,并且可以被修改和重写. (例如,许多Smalltalk方言就是这样做的,它也很像C和汇编中的方式.)

只要你有其中一个,你就可以拥有所有这些,只需在另一个上面实现一个.

JVM有两个:Exceptions和GOTO,但JVM中的GOTO并不是通用的,它非常有限:它只能在单个方法中运行. (它主要用于循环.)因此,这给我们留下了例外.

因此,这是您的问题的一个可能的答案:您可以在例外之上实现协同例程.

另一种可能性是根本不使用JVM的控制流并实现自己的堆栈.

但是,这通常不是在JVM上实现协同例程时实际采用的路径.最有可能的是,实现协同例程的人会选择使用Trampolines并将执行上下文部分重新作为对象.也就是说,例如,如何在CLI上的C♯中实现生成器(不是JVM,但挑战类似). C♯中的生成器(基本上是受限制的半协同例程)是通过将方法的局部变量提升到上下文对象的字段中并在每个yield语句中将该方法拆分为该对象上的多个方法来实现的,将它们转换为状态机,并通过上下文对象上的字段仔细线程化所有状态更改.在async / await作为语言特性出现之前,一个聪明的程序员也使用相同的机制实现了异步编程.

然而,这就是你指出的文章最有可能提到的:所有这些机器都是昂贵的.如果你实现自己的堆栈或将执行上下文提升到一个单独的对象中,或者将所有方法编译成一个巨大的方法并在任何地方使用GOTO(由于方法的大小限制,甚至不可能),或者使用Exceptions作为控件-flow,这两件事中至少有一件是真的:

>您的调用约定与其他语言所期望的JVM堆栈布局不兼容,即您失去了互操作性.
> JIT编译器不知道你的代码到底在做什么,并且提供了字节代码模式,执行流模式和使用模式(例如抛出和捕获大量的异常),它没有预料到并且不知道如何优化,即你失去了表现.

Rich Hickey(Clojure的设计师)曾在一次演讲中说过:“Tail Calls,Performance,Interop.Pick Two.”我把它概括为我称之为Hickey的Maxim:“高级控制流,性能,互操作.选择两个.”

实际上,通常很难实现互操作或性能之一.

此外,您的编译器将变得更加复杂.

当构造在JVM中本地可用时,所有这一切都消失了.想象一下,例如,如果JVM没有Threads.然后,每个语言实现都会创建自己的Threading库,这个库很难,很复杂,很慢,并且不能与任何其他语言实现的Threading库互操作.

一个最近的,现实世界的例子是lambdas:JVM上的许多语言实现都有lambdas,例如:斯卡拉.然后Java也添加了lambdas,但由于JVM不支持lambda,它们必须以某种方式编码,而Oracle选择的编码与Scala之前选择的编码不同,这意味着你无法传递Java lambda期望Scala函数的Scala方法.这种情况下的解决方案是Scala开发人员完全重写了lambda的编码,以便与Oracle选择的编码兼容.这实际上在某些地方打破了向后兼容性.

相关文章

共收录Twitter的14款开源软件,第1页Twitter的Emoji表情 Tw...
Java和Scala中关于==的区别Java:==比较两个变量本身的值,即...
本篇内容主要讲解“Scala怎么使用”,感兴趣的朋友不妨来看看...
这篇文章主要介绍“Scala是一种什么语言”,在日常操作中,相...
这篇文章主要介绍“Scala Trait怎么使用”,在日常操作中,相...
这篇文章主要介绍“Scala类型检查与模式匹配怎么使用”,在日...