Ruby和Javascript之间的主要语义差异是什么?

12
如果要在Javascript引擎的基础上实现Ruby(无论是在浏览器上还是在独立的V8或Spidermonkey之上),那么Ruby和JS对象模型之间的关键阻抗不匹配是什么?

当我查看SpiderMonkey跟踪JIT的架构时,我想到了这个问题 - 它是否也可以作为Ruby实现的核心。编写适用于浏览器的Ruby也是不错的选择(就像GWT为Java所做的那样)- 在这种情况下,我想会有许多限制,包括可使用的库和语言特性。 - Nick Main
我认为到目前为止指出的最大问题(由Marc-André Lafortune在评论中指出)是缺少方法/常量和自动加载功能。未来版本的Ecmascript将包含“代理”,这可能会允许有效实现:http://wiki.ecmascript.org/doku.php?id=harmony:proxies - Nick Main
实际上,我非常确定代理在Firefox 4中的TraceMonkey版本中存在,并且我希望其他实现者也会效仿,因为它们对于安全性来说非常有用。 (例如,请参见Mark S. Miller的SES(安全ECMAScript)和Dr. SES(分布式弹性安全ECMAScript)工作)。 - Jörg W Mittag
TraceMonkey跟踪JIT主要由nanojit组成,这是一种独立于语言的JIT编译器,不是吗?如果你只想要一个带有跟踪JIT的Ruby实现,那么真的没有必要编译成ECMAScript,只需将nanojit连接到Rubinius即可。或者在PyPy中编写一个Ruby实现,它的跟踪JIT非常棒。 (顺便说一句,PyPy有一个ECMAScript后端,因此您实际上可以一箭双雕。更像是四只鸟,因为在PyPy中编写的Ruby实现可以在本地、JVM、.NET和ECMAScript上运行。) - Jörg W Mittag
@Jörg,感谢您指向PyPy - 这看起来像是一个有趣的项目。 - Nick Main
3个回答

7

最明显的是,ECMAScript是基于原型的,而Ruby是基于类和mixin的。此外,在Ruby中,封装是通过对象完成的,在ECMAScript中则是通过闭包。

然而,我猜测Ruby的控制流构造将比其对象模型更具挑战性。毕竟,James Coglan's JS.Class本质上是在ECMAScript中实现了Ruby的对象模型,而它并不是很大。

ECMAScript缺乏构建自己控制流构造所需的工具。通常,您需要使用GOTO、continuations或proper tail calls。如果您拥有其中之一,您可以轻松地实现其他所有内容:异常、循环、开关、线程、Fiber、生成器、协同程序等等。

但 ECMAScript 并没有这些控制流结构(至少在 GOTO 的情况下有很好的理由)。ECMAScript 唯一拥有足够强大的控制流结构来构建其他结构的是异常。不幸的是,它们非常慢。(尽管如此,它们已被用作实现基础,例如在微软 Live Labs Volta 编译器中,该编译器使用 ECMAScript 异常来实现 .NET 异常、迭代器、生成器甚至线程。)
因此,基本上你必须至少实现自己的调用堆栈,如果不是整个解释器(就像 HotRuby 一样),执行全局 CPS 转换或类似的操作。
基本上,你希望运行在 ECMAScript 之上的 Ruby 引擎是:
  1. 一个忠实实现 RubySpec(特别是控制流结构,如线程、纤程、throw/catch、异常等)的引擎,
  2. 性能和
  3. 与 ECMAScript 紧密集成(即能够在两种语言之间传递对象和调用方法)。

不幸的是,当你不得不采取一些技巧,比如管理自己的堆栈、进行CPS变换、建立在异常之上时,结果你只能选择其中的两个目标。


所有的goto恨是怎么回事? - dsimcha
1
@dsimcha:你对“憎恨”的定义很奇特。 - Jörg W Mittag
您可能需要复制一些方法从一个对象到另一个对象来模拟混入。感谢提醒关于生成器等 - 我会考虑如何在不使用异常的情况下模拟它们。 - Nick Main
我认为基于对象和基于闭包的封装之间的差异并不是很大。在JS世界中,有许多技巧可以实现私有方法和属性,这些技巧似乎是适用的。 - Nick Main

3
  1. Ruby使用块级作用域来定义局部变量,而JavaScript使用函数级作用域。
  2. 使用Ruby的继承和混入可能会在JavaScript的原型继承中遇到问题。
  3. Ruby对于方法/lambda调用的参数数量检查更加严格,而JavaScript则允许传递参数时更加宽松。
  4. Ruby有真正的常量并且可以强制执行,但是JavaScript可能没有(这取决于解释器使用的版本)。
  5. 类变量(呃)在JavaScript中没有相应的等价物,因此需要特殊处理。
  6. Ruby核心库中有绿色线程,而JavaScript则没有。

好的列表。我肯定会补充说明,像method_missingconst_missingautoload这样的钩子的兼容性也意味着每个方法调用或常量查找都必须被包装(比如说myobject._call_method_("foo", ..))。 - Marc-André Lafortune
模式 function(){..}()(即立即调用内联匿名函数)在 JS 世界中并不陌生 - 这将允许模拟块作用域。 - Nick Main
将一个 mixin 添加到 JS 对象中需要将 mixin 中的方法复制到对象中,这会带来一定的成本 - 这之外还有其他需要注意的地方吗? - Nick Main
我知道JRuby使用本地操作系统线程而不是绿色线程 - 这会引起问题吗?在浏览器中,线程不会是一个很大的需求,或者需要显式使用Web Workers,因此可能不是一个大问题。 - Nick Main
@Marc-André - “缺失”的处理程序似乎是最大的问题。在调用或使用之前,它们需要对每个引用进行空值检查,这将是昂贵的。感谢您指出这一点。 - Nick Main
关于通过复制方法实现 Mixin,这不一定能反映 Mixin 的“动态”本质(重新打开 Mixin 并在混合后添加方法)。 - Phrogz

2

JavaScript是图灵完备的,因此理论上您可以在其中实现任何内容,包括其他编程语言。 无论JavaScript的实现和目标语言(Ruby)有多么不同。像Ruby和C这样的语言之间的阻抗不匹配非常巨大,所以你有Ruby、Python、Perl和JavaScript本身,所有这些都是用C实现的。

在JavaScript中实现Ruby应该比在低级语言中实现Ruby容易几个量级。你的优势在于Ruby和Ruby的标准库的很多部分都是用Ruby本身编写的,因此一旦你有了基本解释器,事情应该逐渐变得更加容易。

在JavaScript中实现一个高效的Ruby解释器可能会更难,但仍然有可能。您最终可能会将Ruby翻译成JavaScript,以便使用可用的优秀优化器。

因此,请不要考虑Ruby和JavaScript之间的差异。看看Ruby的标准实现,并考虑如何在JavaScript中实现那个


1
谢谢你的回答 - 我更多地考虑如何从现有的Ruby实现交叉编译到Javascript,而不是在浏览器中实现完整的Ruby解释器/REPL。 - Nick Main
@Nick 交叉编译将需要与解释器大部分工作相同的工作,因此请采取相同的方法。查看 Ruby 在 C 和/或 Java 实现中的运行时支持,并考虑如何在 JavaScript 中实现。不是翻译代码,而是找出在运行时支持 Ruby 所需的数据结构和功能。 - Apalala

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接