使用JRuby/Jython实现Ruby/Python互操作?
这可能是个傻问题,因为我对Java、Jython、JRuby和字节码了解不多,不过我还是想问一下。
今天我又发现了_why's unholy这个项目。它可以让你从Ruby代码输出Python的字节码,也就是说,它们可以生成相同的字节码。
Jython会输出Java的字节码,JRuby也是如此。既然这两者都编译成相同的字节码,那是不是意味着你可以在Ruby中使用任何Python库,反之亦然,也可以在Python中使用Ruby的库呢?
2 个回答
有两种方法可以做到这一点。两种方法都可以把代码静态编译成真正的Java类。根据我的了解,Jython在这个情况下会生成Java源代码,然后通过一个叫jythonc的脚本来调用javac。不过,这样做需要编译。
对于这两种解释器,你可以在脚本中调用Java代码,也可以把解释器嵌入到Java应用程序中。
比如说,想从Python调用Java代码:
>>> from java.util import Random
>>> r = Random()
>>> r.nextInt()
501203849
要在Java中嵌入JRuby解释器,你可以这样做(注意,还有一种基于JSR223的方法,这个是核心方法):
package vanilla;
import org.jruby.embed.ScriptingContainer;
public class HelloWorld {
private HelloWorld() {
ScriptingContainer container = new ScriptingContainer();
container.runScriptlet("puts Hello world");
}
public static void main(String[] args) {
new HelloWorld();
}
你也可以用Jython做同样的事情(我想你需要正确设置jruby的路径):
import org.jruby.embed.ScriptingContainer
container = ScriptingContainer()
container.runScriptlet("puts Hello world")
反过来也可以做到这一点。
不过,单纯通过导入是无法把整个Ruby标准库导出到Python解释器中的。你需要提前把Ruby的标准库编译成字节码。
不过,使用上面提到的技术,加上一些辅助脚本和定义的接口,你可以在两种语言之间桥接特定的功能。
不,这样做是行不通的。至少不是你想的那样。
Jython和JRuby之间的互操作性就像CPython和YARV之间的互操作性一样:它们都运行在同一个平台上,所以可以通过这个平台相互通信。
在CPython和YARV的情况下,这个平台是C/POSIX,因此它们可以通过C结构体、int
、char*
和C函数调用进行通信。而在Jython和JRuby的情况下,这个平台是JVM,所以它们可以通过JVM对象、JVM类、JVM接口、JVM类型和JVM方法进行通信。
在这两种情况下,这些平台的基本元素看起来与Python或Ruby对象完全不同。
对JRuby来说,Jython只是另一个Java程序。对Jython来说,JRuby也是另一个Java程序。
举个例子:在Ruby中,你可以随时动态地添加、删除和重新定义方法。而在JVM上,能够动态添加和删除的最小代码单位是类。因此,Ruby方法实际上并不是作为Java方法来表示的,而是作为Java的一个类来表示的。从逻辑上讲,一个有几个方法的Ruby对象在Java中表示为一个没有方法的Java对象,只有一个Dictionary<String, RubyMethod>
字段。换句话说:从Java来看,这完全不可用,而从JRuby的角度来看,Jython也只是Java,所以从Jython来看也是不可用的。
不过,确实有一些方法可以稍微改善这种情况。你可以使用实际的Java类型在两者之间进行通信——这两种实现与Java的互操作性都很好。因此,与你直接传递Ruby哈希给Python或Python字典给Ruby不同,你可以在Ruby和Python中使用Java的Map
。但请注意,这要求你的Ruby和Python代码都特别编写为在JVM上工作。换句话说:你不能随便使用网上找到的任何Python或Ruby库,这正是你所询问的。
另一个可能性是@duncan在他的回答中提到的:将Jython或JRuby嵌入到你的Ruby或Python应用程序中作为脚本引擎。但同样,这并没有真正回答你关于从Ruby使用任意Python库或反之的提问。
那么,问题出在哪里呢?
问题在于,为了让两个运行时能够通信,它们需要说同一种“语言”。而在这个特定的情况下,这两个运行时唯一共同的语言是Java,或者说是一个严重简化的Java子集。
所以,我们需要找到一种共同语言。定义这样一种语言的一种方法是让两个运行时都理解对方的元对象协议(MOP)。
MOP基本上是语言对象模型的对象模型。嗯,这有点混淆,因为我们用“对象模型”这个词来表示两种不同的东西。让我换个说法:
MOP基本上是语言对象系统的领域模型。就像一个银行系统的领域模型包含表示现实世界客户、账户、余额、账本等的对象,以及表示现实世界行为(如资金转移、取款等)的方法,MOP包含表示语言类、方法、变量、对象的对象,以及表示语言行为(如查找变量、调用方法、继承类、构造类实例等)的方法。
通常,每个运行时都将其MOP保密,并且每个运行时都有自己的MOP。
如果JRuby和Jython能够相互暴露它们的MOP并理解对方的MOP(或者更好的是:它们将MOP暴露给JVM并都使用相同的 MOP),那么你就可以将那些奇怪的JRuby方法包传递给Jython,它就知道如何找到属于该对象的方法以及如何调用它们,因为它可以直接询问JRuby的MOP该怎么做。
实际上,有一个项目旨在为JVM创建这样一个MOP:dynalang MOP是一个为在JVM上运行的动态语言提供共享、标准化MOP的项目。这个项目由Mozilla Rhino ECMAScript引擎的维护者Attila Szegedi创建。目前,没有哪个大型语言实现使用它,但至少Rhino、JRuby、Jython和Groovy之间正在进行合作,以确保dynalang足够通用,可以支持所有不同语言的对象模型。
如果你想提前了解一下拥有这样一个共享MOP的世界会是什么样子,可以看看微软的动态语言运行时(DLR)。DLR包含这样的MOP,所有支持DLR的运行时(除了通常的那些,比如IronRuby、IronPython、IronJS和IronScheme,现在还包括C# 4和Visual Basic.NET 10)几乎可以无缝地相互操作。
另一个类似的平台是Parrot虚拟机,它专门设计用于允许多种动态语言在同一运行时平台上互操作。现在有Python(Pynie)和Ruby(Cardinal)的实现,但尤其是Cardinal距离一个完整的Ruby实现仍然很远。