Ruby 开发者使用 Crystal 的 5 大理由
这是一篇由 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
答:这是一个陷阱问题 - 都是。上面的模块可以在 Ruby 或 Crystal 中运行。多么酷!
查看更多代码相似性的示例 这里。
这并不意味着所有 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
方法是如何工作的?
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 编程语言,以下是一些不错的入门资源
- Crystal Play - 一个在线 REPL 类工具,用于 Crystal
- 安装 Crystal
- Crystal for Rubyists
- Crystal Exercisms
- 在几分钟内创建自己的 HTTP 服务器