跳至内容
GitHub 存储库 论坛 RSS-新闻源

告别 Ruby 星期四

Ary Borenzweig

如果您还不知道,Crystal 是一种编程语言,其语法和语义类似于 Ruby,但它不是解释型的,它将程序编译成原生代码。

因此,我们选择用 Ruby 实现 Crystal 的编译器。为什么?

  • Ruby 是一种很棒的语言,语法优雅,适合非常快速的原型设计。
  • 有一天,我们可以用 Crystal 编写编译器,如果语法和语义与 Ruby 相似,移植代码应该相对容易。

我们尝试过很多次用 Crystal 实现编译器。但我们总是遇到问题。

编译时间太长

我们起初认为这是因为 Ruby 有时很慢。

然而,在开始的时候,语言是纯粹的,非常非常类似于 Ruby,在那里你永远不需要指定类型。编译时间相对于代码大小和数组实例化数量呈指数增长。尝试编译编译器的一部分开始需要几分钟。我们认为这是不可接受的。

因此,我们做出了一点牺牲:有时您必须指定数组、哈希和其他泛型类型的类型。

a = []          # OK for Ruby, but not for Crystal
b = [] of Int32 # OK for Crystal

c = [1, 2, 3]   # OK for Ruby and for Crystal
c << 4          # OK for Ruby and for Crystal
c << "hello"    # OK for Ruby, error for Crystal (c is Array(Int32))

d = [1, 2, 3] of Int32 | String
d << "hello"    # OK for Crystal

有了这个小小的改变,编译时间有了很大的改善,相对于代码大小呈线性增长。

但我们仍然无法完成新的编译器。

缺少功能

在用 Ruby 编写的编译器中,我们使用了 Array、Hash 和 Set。我们访问了文件系统,并使用了与 LLVM 的绑定。除非我们在我们的语言和标准库中拥有相同的特性,否则我们永远无法用 Crystal 实现编译器。

因此,我们在标准库中添加了许多功能。我们添加了与 C 的绑定。我们有 C 结构体和联合体。我们有函数指针。所有这些都在 Crystal 中指定,无需用其他语言编写。

但是……

漏洞

编译器并不完美。它存在(并且仍然存在)漏洞。对于小型示例和我们的测试来说,一切正常,但用 Crystal 编写编译器确实开始对用 Ruby 编写的编译器进行测试,并暴露了许多漏洞和缺少的特性。

因此,我们修复了这些漏洞中的大多数,并添加了缺少的特性。

但所有这一切总是意味着

落后于编译器

我们在用 Ruby 编写的编译器中引入的每个漏洞修复或新特性都必须移植到用 Crystal 编写的编译器中(它仍然无法成功编译)。而且我们时不时地想要向语言添加新特性,而且我们也这么做了,所以我们开始越来越落后。

所以有一天,我们决定冻结功能(也冻结漏洞,除非我们可以解决问题)。

这是一条漫长的道路,但也是一条非常有趣和有启发性的道路

  • 将代码从 Ruby 移植到 Crystal 非常容易,大多数情况下只需要很少的修改。
  • 移植后的代码与 Ruby 中的行为完全相同。我们仍然惊叹于我们能够如此完美地捕捉 Ruby 的语法和语义。
  • 移植后的代码揭示了用 Ruby 编写的编译器中的漏洞,这意味着,理论上,Crystal 可以帮助您拥有更加健壮和正确的代码。

今天,星期四,我们终于做到了。我们设法用 Crystal 本身编写了 Crystal 的编译器。耶!新的编译器可以成功地编译自身,这个新的编译器可以编译自身,生成的二进制文件与旧的二进制文件完全相同。它还可以编译其规范,并且它们全部通过。

Crystal 的未来一片光明

它更快

用 Ruby 编写的编译器大约需要 20 秒来推断编译器的类型。用 Crystal 编写的编译器大约需要 2.8 秒来完成相同的事情。

请记住:我们在这里谈论的是全局类型推断。2.8 秒来对编译器进行语义分析。一点也不差!

而且我们还有很多优化需要应用,无论是编译器生成的代码,还是编译器和标准库中存在的代码(例如,Hash 的实现非常幼稚)。

我们不再依赖 Ruby

因为我们现在有了用 Crystal 编写的编译器,我们可以只使用 Crystal 编译编译器的新版本。再见,Ruby。很高兴有你加入我们的团队,但是,嗯,对你来说,我们有点太慢了。是的,是的,我们喜欢你的很多东西,但我们在这里说的是编译器。你不能让程序员等几分钟甚至更长时间来编译他们的程序。哦,也许我们对你有点苛刻。你知道吗?不要走。回来。你仍然可以帮助我们使用你的朋友 Rails 开发出色的 Web 应用程序前端。我们是认真的。你在这个方面很出色。我们不太确定我们的后端。Erlang 和 Go 在这方面真的很棒。可惜它们的语法(和语义)不像你那么好。是否有一天会有一种像你一样的语言,但速度像原生代码一样快?可能不会。但如何看待一种类似的语言,如果你来自 Ruby,你需要做出一些小小的牺牲?我们真的希望如此。

路线图

现在我们必须修复编译器中剩余的漏洞。我们不喜欢有漏洞的软件,所以我们不会只是继续向语言添加特性,除非我们让它坚如磐石。

然后我们可以开始考虑并发性、更好的宏、更好的函数指针、真正的结构体、命名参数、元组、纤维、调试器……