跳至内容
GitHub 代码库 论坛 RSS-新闻源

Ruby 开发者使用 Crystal 的 5 大理由

Mark Siemers

这是一篇由 Mark Siemers 的客座文章,他自愿提出撰写一系列 Crystal 博客文章。预计会有更多文章出现,或者 - 更好的是 - 联系我们,撰写您自己的文章。

1. 极低的学习曲线

想想过去 5-10 年流行起来的几种语言。你会想到什么?Elixir、Go、Rust 等等?它们在性能上都优于 Ruby,但学习和掌握起来更困难。

如果可以以更简单的学习曲线获得性能提升呢?

有多简单?让我们看一些代码。

问:以下哪些模块是用 Ruby 编写的?哪些是用 Crystal 编写的?

module Year
  def self.leap?(year)
    year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)
  end
end
module Hamming
  def self.distance(a,b)
    a.chars.zip(b.chars).count{|first, second| first != second }
  end
end

答:这是一个陷阱问题 - 都是。上面的模块可以在 RubyCrystal 中运行。多么酷!

查看更多代码相似性的示例 这里

这并不意味着所有 Ruby 代码都能在 Crystal 中运行(反之亦然),但您可以立即在 Crystal 中完成很多事情,并在第一天、甚至第一分钟就投入使用。

Crystal(一种强类型编译语言)是如何像 Ruby(一种动态鸭子类型语言)一样工作的?Crystal 的编译器使用两种强大的技术组合:类型推断联合类型。这使编译器能够读取类似 Ruby 的代码并推断出要使用的正确类型。

除了相似之处,Crystal 还提供了一些比 Ruby 更核心的优势。比如…

2. 编译时检查和方法重载

在 Ruby 中编写 is_a?respond_to? 以确保代码不会崩溃,这是否感觉很笨拙?您是否担心所有没有进行这些检查的地方?这些都是潜在的错误吗?

Crystal 是一种编译语言,会在编译时检查所有方法输入和输出。如果任何类型不匹配,它们将在运行时之前被捕获。

让我们回到上面的 Year::leap? 示例。在 Ruby 中,当输入不是整数时会发生什么?

Year.leap?("2016") #=> false
Year.leap?(Date.new(2016, 1, 1)) #=> undefined method `%' for #<Date: 2016-01-01 ... >

对于 String,我们会得到错误的答案,对于 Date,我们会得到运行时异常。在 Ruby 中修复问题至少需要一个 is_a? 语句

module Year
  def self.leap?(input)
    if input.is_a? Integer
      input % 400 == 0 || (input % 100 != 0 && input % 4 == 0)
    elsif input.is_a? Date
      input.leap?
    else
      raise ArgumentError.new("must pass an Integer or Date.")
    end
  end
end

该方法 看起来好吗?我们仍然有可能发生运行时异常,只是错误消息更有帮助。

在 Crystal 中,我们可以选择显式地键入我们的输入(和输出)。我们可以将方法签名更改为 self.leap?(year : Int),并确保输入为整数。

我们在编译时而不是运行时得到有用的消息

Year.leap?("2016")
Error in line 10: no overload matches 'Year.leap?' with type String
Overloads are:
 - Year.leap?(year : Int)

如果我们想在我们的模块中添加对 Time(想想 Ruby 中的 DateTime)的支持,我们可以重载 Year::leap?

module Year
  def self.leap?(year : Int)
    year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)
  end

  def self.leap?(time : Time)
    self.leap?(time.year)
  end
end

与 Ruby 一样,方法重载允许输入灵活,但没有鸭子类型的猜测。编译时检查可以防止类型不匹配错误进入生产环境。

说到生产环境,还有…

3. 惊人的性能

编译的另一个优势是速度和优化。在比较 Ruby 和 Crystal 的性能时,通常用数量级而不是百分比来表示。

在一个例子中,在 Crystal 中对随机数求和 可以比 Ruby 快 10 个数量级(约快 3700 万%)。这是由于编译器优化和在 Crystal 中使用基本数据类型的能力。这确实存在整数溢出的风险,对于大数字来说 (参见 Ary 的解释)。

Crystal 的内置 HTTP 服务器在基准测试中能够处理每秒超过 200 万个请求。许多 Web 框架始终为 Web 应用程序提供毫秒级的响应时间。

这引出了下一个要点…

4. 你想要的 Web 框架已经存在了

喜欢 Rails(甚至 Elixir 的 Phoenix)的完整性吗?你会在 Amber 框架中找到宾至如归的感觉。

Sinatra 的简单性和易于定制更适合您吗?您将在 Kemal 中找到这种简单性。

您是否想要一个利用编译时检查来进行强参数、HTTP 动词和数据库查询的全栈框架?这是您的 幸运 日。

在一月份,每个 Web 框架都将在他们自己的专用文章中得到重点介绍。请返回 Crystal 博客以了解更多信息。

5. Crystal 是用 Crystal 编写的!它易于理解和贡献

您是否曾经阅读过 Ruby 的源代码?尝试弄清楚一些 Enumerable 方法是如何工作的?

Ruby 的 Enumerable#all? 实现

static VALUE
enum_all(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo);
    return memo->v1;
}

需要多长时间才能弄清楚这段代码在做什么?如果您从未使用过 C 代码,可能需要很长时间。

将它与 Crystal 的 Enumerable#all? 实现 相比

def all?
  each { |e| return false unless yield e }
  true
end

弄清楚这段代码需要多长时间?如果您了解 Ruby 或 Crystal,可能只需要几秒钟。

考虑到这一点,请记住98.4% 的 Crystal 是用 Crystal 编写的,只有 0.3% 是用 C++ 编写的。

Ruby 用 C 编写了 30.6%,用 Ruby 编写了 64.8%。

实际上,Crystal 中只有一份文件是用 C++ 编写的,因此只要您没有更改 LLVM 扩展,您在 Crystal 语言中寻找的任何东西几乎可以肯定是用 Crystal 编写的。

阅读、理解和为 Crystal 做贡献比您能找到的任何语言都容易。

从哪里开始?

如果您想尝试 Crystal 编程语言,以下是一些不错的入门资源